diff --git a/.gitattributes b/.gitattributes index 5e42ccdfc11..a2047fd34cc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,4 @@ /.github/ export-ignore /benchmark/ export-ignore /core-tests/ export-ignore -/.travis.yml export-ignore +*.h linguist-language=cpp diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index b1f6ccf28e7..00000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,14 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] - -custom: ['https://gitee.com/swoole/swoole?donate=true'] diff --git a/.github/ISSUE-CN.md b/.github/ISSUE-CN.md index 0c511db2799..c08b7485d95 100644 --- a/.github/ISSUE-CN.md +++ b/.github/ISSUE-CN.md @@ -5,11 +5,11 @@ # 需知 -当你觉得发现了一个Swoole内核的bug时, 请提出报告. +当你觉得发现了一个Swoole内核的BUG时, 请提出报告. Swoole的内核开发者们或许还不知道问题的存在, 除非你主动提出报告, 否则BUG也许将很难被发现并修复, 你可以在[Github的issue区](https://github.com/swoole/swoole-src/issues)提出错误报告(即点击右上角绿色的`New issue`按钮), 这里的错误报告将会被最优先解决. -请不要在邮件列表或私人信件中发送错误报告, Github的issue区同样可以提出对于Swoole的任何要求与建议. +请不要在邮件列表或私人信件中发送错误报告, GitHub的issue区同样可以提出对于Swoole的任何要求与建议. 在你提交错误报告之前, 请先阅读以下的**如何提交错误报告**. @@ -42,6 +42,16 @@ Please answer these questions before submitting your issue. Thanks! 其中, 最为关键的是提供**可稳定重现的简单脚本代码**, 否则你必须提供尽可能多的其它信息来帮助开发者判断错误原因 +## 内存分析 (强烈推荐) + +更多时候, Valgrind比gdb更能发现内存问题, 通过以下指令运行你的程序, 直到触发BUG + +```shell +USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php +``` + +* 当程序发生错误时, 可以通过键入 `ctrl+c` 退出, 然后上传 `/tmp/valgrind.log` 文件以便于开发组定位BUG. + ## 关于段错误(核心转储) 此外, 在一种特殊情况下你可以使用调试工具来帮助开发者定位问题 @@ -77,13 +87,3 @@ gdb php /tmp/core.4596 ``` 将以上信息都贴在issue中 - -#### 内存分析 (强烈推荐) - -更多时候, Valgrind比gdb更能发现内存问题, 通过以下指令运行你的程序, 直到触发BUG - -```shell -USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php xxx.php -``` - -* 当程序发生错误时, 可以通过键入 `ctrl+c` 退出, 然后上传 `/tmp/valgrind.log` 文件以便于开发组定位BUG. diff --git a/.github/ISSUE.md b/.github/ISSUE.md index ce81bea2b39..3cbe4908678 100644 --- a/.github/ISSUE.md +++ b/.github/ISSUE.md @@ -27,6 +27,16 @@ Please answer these questions before submitting your issue. Thanks! ``` The most important thing is to provide a simple script for reproducing the error, otherwise, you must provide as much information as possible. +## Memory detection (recommended) + +In addition to using `gdb` analysis, you can use the `valgrind` tool to check if the program is working properly. + +```shell +USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php your_file.php +``` + +* After the program is executed to the wrong location, `ctrl+c` is interrupted, and upload the `/tmp/valgrind.log` file. + ## CoreDump Besides, In a special case, you can use debugging tools to help developers locate problems @@ -60,16 +70,7 @@ Use the f command in gdb to view the code segment corresponding to the ID. ``` If there is no function call stack information, it may be that the compiler has removed the debug information. Please manually modify the `Makefile` file in the swoole source directory and modify CFLAGS to -```shell -CFLAGS = -Wall -pthread -g -O0 -``` - -#### Memory detection (recommended) - -In addition to using `gdb` analysis, you can use the `valgrind` tool to check if the program is working properly. ```shell -USE_ZEND_ALLOC=0 valgrind --log-file=/tmp/valgrind.log php xxx.php +CFLAGS = -Wall -pthread -g -O0 ``` - -* After the program is executed to the wrong location, `ctrl+c` is interrupted, and upload the `/tmp/valgrind.log` file. 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 95274f1c6aa..494bb252deb 100644 --- a/.github/workflows/alpine.Dockerfile +++ b/.github/workflows/alpine.Dockerfile @@ -1,20 +1,23 @@ -FROM hyperf/hyperf:7.4-alpine-v3.11-dev +ARG PHP_VERSION +ARG ALPINE_VERSION -LABEL maintainer="Swoole Team " version="1.0" license="MIT" +FROM phpswoole/php:${PHP_VERSION}-alpine -COPY . /opt/www +LABEL maintainer="Swoole Team " version="1.0" license="Apache2" -WORKDIR /opt/www +ARG PHP_VERSION + +COPY . /swoole + +WORKDIR /swoole RUN set -ex \ - && ln -sf /usr/bin/phpize7 /usr/local/bin/phpize \ - && ln -sf /usr/bin/php-config7 /usr/local/bin/php-config \ && phpize \ - && ./configure --enable-mysqlnd --enable-openssl --enable-http2 \ - && make -s -j$(nproc) && make install \ - && echo "extension=swoole.so" > /etc/php7/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 new file mode 100644 index 00000000000..c223a1333b7 --- /dev/null +++ b/.github/workflows/coverity.yml @@ -0,0 +1,58 @@ +name: coverity-scan + +on: + push: + branches: + - coverity_scan + +env: + COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} + COVERITY_SCAN_EMAIL: team@swoole.com + COVERITY_PROJECT: ${{ github.repository }} + COV_TOOLS_DIR: ${{ github.workspace }}/cov-analysis-linux64 + COV_BUILD_DIR: ${{ github.workspace }} + COV_RESULTS_DIR: cov-int + COV_RESULTS_FILE: analysis-results.tgz + +jobs: + coverity-scan: + if: github.repository_owner == 'swoole' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "8.0" + + - name: Download and install Coverity Build Tool + run: | + wget -q https://scan.coverity.com/download/linux64 \ + --post-data "token=${COVERITY_SCAN_TOKEN}&project=${COVERITY_PROJECT}" \ + -O cov-analysis-linux64.tar.gz + mkdir ${COV_TOOLS_DIR} + tar xzf cov-analysis-linux64.tar.gz --strip 1 -C ${COV_TOOLS_DIR} + ls -la ${COV_TOOLS_DIR}/bin + + - name: Run build steps + run: | + sudo phpize && ./configure + + - name: Run Coverity Scan Analysis Tool + run: | + export PATH=${COV_TOOLS_DIR}/bin:$PATH + cd ${COV_BUILD_DIR} + cov-build --dir ${COV_RESULTS_DIR} make -j 4 + + - name: Upload Coverity Scan Analysis results + run: | + cd ${COV_BUILD_DIR} + tar czf ${COV_RESULTS_FILE} ${COV_RESULTS_DIR} + curl \ + --form project=${COVERITY_PROJECT} \ + --form token=${COVERITY_SCAN_TOKEN} \ + --form email=${COVERITY_SCAN_EMAIL} \ + --form file=@${COV_RESULTS_FILE} \ + --form version=${GITHUB_SHA} \ + --form description="GitHub Actions" \ + https://scan.coverity.com/builds?project=${COVERITY_PROJECT} diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 462df02dc98..b9232561366 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -1,96 +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 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 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: [ '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 + run: | + cp .github/workflows/alpine.Dockerfile alpine.Dockerfile + 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/gitee.yml b/.github/workflows/gitee.yml deleted file mode 100644 index d500e2b4a28..00000000000 --- a/.github/workflows/gitee.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Mirror to Gitee Repo - -on: [ push, delete, create ] - -jobs: - git-mirror: - runs-on: ubuntu-latest - steps: - - name: Configure Private Key - if: github.repository_owner == 'swoole' - 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 - if: github.repository_owner == 'swoole' - 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/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 acd4b65f602..00000000000 --- a/.github/workflows/lib.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: lib-swoole - -on: [push, pull_request] - -env: - CODECOV_TOKEN: "50513381-198e-43d3-a8b1-e36b95038927" # from: https://codecov.io/gh/swoole/swoole-src - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v1 - - - name: install dependencies - run: sudo apt install -y googletest libgtest-dev redis-server libboost-stacktrace-dev && - cd /usr/src/googletest/googletest && - sudo mkdir build && - cd build && - sudo cmake .. && - sudo make -j$(sysctl -n hw.ncpu) && - sudo make install && - cd - && - sudo rm -rf /usr/src/googletest/googletest/build - - - name: configure - run: phpize && ./configure --enable-sockets --enable-mysqlnd --enable-http2 - - - name: make - run: cmake . -DCODE_COVERAGE=ON && - make VERBOSE=1 -j$(sysctl -n hw.ncpu) && - 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 && - sudo curl -s https://codecov.io/bash -o codecov.sh && - sudo chmod +x codecov.sh && sudo ./codecov.sh -f coverage.info -t ${CODECOV_TOKEN} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 59d5aee1ee5..00000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: run-swoole-test - -on: [push, pull_request] - -jobs: - run-swoole-test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - php: [7.2, 7.3, 7.4, 8.0, 8.1] - 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: | - ${{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 ead1c55768a..9068deb313d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,13 +7,14 @@ *.so *.loT *.pid -/Debug/* +*.dep modules/* /.deps /.libs/ /core /examples/core /Debug +/Release /CMakeFiles /cmake_install.cmake /CMakeCache.txt @@ -79,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 @@ -91,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 @@ -105,3 +115,6 @@ core-tests/samples/CMakeFiles/ *.gcda *.info /html +/tests/include/lib/vendor/ +/tests/include/lib/composer.lock +/scripts/data diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a77299349b9..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,66 +0,0 @@ -language: php - -compiler: - - gcc - - clang - -os: - - linux - -php: - - 7.2 - - 7.3 - - 7.4 - - nightly - -matrix: - fast_finish: true - allow_failures: - - php: nightly - -notifications: - email: team@swoole.com - -sudo: required - -env: - global: - - secure: "Mqg9ifSV0BLv2TIBw/x64aEuB8Y5aPXwXg7xAOq08oPlHBYSBgSYxroJL5PXs0fe7PszZF20bUBinwmEXYQzqgP/40Y1kmq/kfTsDXwTOc9qbAbA1pWvH+Sk1kDP5MLJRPWBCkqctyFd0I0u0SdzT1fgSqirqEz2bMnbAUVpSvo=" - - CODECOV_TOKEN=:50513381-198e-43d3-a8b1-e36b95038927 - -services: - - docker - -before_install: - - echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca- - -addons: - coverity_scan: - project: - name: "swoole/swoole-src" - description: "Build submitted via Travis CI" - notification_email: team@swoole.com - build_command_prepend: "./configure; make clean" - build_command: "make -j 4" - branch_pattern: coverity_scan - -# compile -before_script: - - date - - env - - uname -a - - ulimit -a - - php -v - - ls -al - - pwd - - echo "`git log -20 --pretty --oneline`" - - echo "`git log -10 --stat --pretty --oneline`" - - ./travis/pecl-install.sh - - ./travis/simple-compile.sh - - php --ri swoole - -script: - - ./travis/route.sh - -after_success: - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ff0f572d286..008a4901608 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,18 +1,22 @@ PROJECT(libswoole) +cmake_minimum_required(VERSION 2.8.12) ENABLE_LANGUAGE(ASM) -SET(SWOOLE_VERSION 4.6.0-alpha) +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) set(CMAKE_MACOSX_RPATH 1) set(SWOOLE_LINK_LIBRARIES pthread dl) if (APPLE) set(CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup") + include_directories(BEFORE /usr/local/include) + link_directories(BEFORE /usr/local/lib) else() list(APPEND SWOOLE_LINK_LIBRARIES rt crypt) endif() @@ -40,11 +44,14 @@ endif(CODE_COVERAGE) file(GLOB_RECURSE SRC_LIST FOLLOW_SYMLINKS src/*.c src/*.cc thirdparty/boost/asm/combined.S + thirdparty/hiredis/alloc.c thirdparty/hiredis/async.c thirdparty/hiredis/hiredis.c 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) @@ -59,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 @@ -82,6 +89,22 @@ if (DEFINED brotli_dir) link_directories(${brotli_dir}/lib) endif() +foreach (LINE ${SWOOLE_CONFIG_FILE}) + if ("${LINE}" MATCHES "define SW_USE_CARES 1") + message(STATUS "enable c-ares") + list(APPEND SWOOLE_LINK_LIBRARIES cares) + endif() +endforeach() + +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}") @@ -104,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) @@ -123,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 @@ -143,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 94709c2b791..00000000000 --- a/README-CN.md +++ /dev/null @@ -1,532 +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) -[![Build Status](https://api.travis-ci.org/swoole/swoole-src.svg)](https://travis-ci.org/swoole/swoole-src) -[![License](https://img.shields.io/badge/license-apache2-blue.svg)](LICENSE) -[![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.0.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`方法) - -### 升级 - -> ⚠️ 如果你要从源码升级, 别忘记在源码目录执行 `make clean` - -1. `pecl upgrade swoole` -2. `git pull && cd swoole-src && 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 ca208b36268..991e85f5468 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,142 @@ -English | [中文](./README-CN.md) - -# Swoole +

+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) +[![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) -[![Latest Version](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases) -[![Build Status](https://api.travis-ci.org/swoole/swoole-src.svg)](https://travis-ci.org/swoole/swoole-src) -[![License](https://img.shields.io/badge/license-apache2-blue.svg)](LICENSE) +[![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) -[![codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src) -![](./mascot.png) +## ⚙️ 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 -**Swoole is an event-driven asynchronous & coroutine-based concurrency networking communication engine with high performance written in C++ for PHP.** +* `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` -## ✨Event-based + +## 🛠 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 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.0.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,12 +566,12 @@ 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 && \ -cd swoole-src && \ -git checkout v4.x.x +git clone --branch v6.0.0 --single-branch https://github.com/swoole/swoole-src.git && \ +cd swoole-src ``` Compile and install at the source folder: @@ -474,15 +592,15 @@ 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-curl` ### Upgrade > ⚠️ If you upgrade from source, don't forget to `make clean` before you upgrade your swoole 1. `pecl upgrade swoole` -2. `git pull && cd swoole-src && make clean && make && sudo make install` +2. `cd swoole-src && git pull && make clean && make && sudo make install` 3. if you change your PHP version, please re-run `phpize clean && phpize` then try to compile ### Major change since version 4.3.0 @@ -490,8 +608,8 @@ After compiling and installing to the system successfully, you have to add a new Async clients and API are moved to a separate PHP extension `swoole_async` since version 4.3.0, install `swoole_async`: ```shell -git clone https://github.com/swoole/async-ext.git -cd async-src +git clone https://github.com/swoole/ext-async.git +cd ext-async phpize ./configure make -j 4 @@ -500,24 +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__: -+ __中文社区__: -+ __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 dab1473a8d9..00000000000 --- a/SUPPORTED.md +++ /dev/null @@ -1,20 +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 | 2020-7-31 | -| [v4.5.x](https://github.com/swoole/swoole-src/tree/master) | 2019-12-20 | 2020-12-31 | 2021-3-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 new file mode 100644 index 00000000000..3aaaf7fa7e4 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,8 @@ +ignore: + - "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 47469cfc6dd..82a8645d512 100644 --- a/config.m4 +++ b/config.m4 @@ -12,51 +12,140 @@ 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 +----------------------------------------------------------------------+ -PHP_ARG_ENABLE(debug-log, enable debug log, -[ --enable-debug-log Enable swoole debug log], no, no) - -PHP_ARG_ENABLE(trace-log, enable trace log, -[ --enable-trace-log Enable swoole trace log], no, no) - -PHP_ARG_ENABLE(sockets, enable sockets support, -[ --enable-sockets Do you have sockets extension?], no, no) - -PHP_ARG_ENABLE(openssl, enable openssl support, -[ --enable-openssl Use openssl], no, no) - -PHP_ARG_ENABLE(http2, enable http2.0 support, -[ --enable-http2 Use http2.0], no, no) - -PHP_ARG_ENABLE(swoole, swoole support, -[ --enable-swoole Enable swoole support], [enable_swoole="yes"]) - -PHP_ARG_ENABLE(mysqlnd, enable mysqlnd support, -[ --enable-mysqlnd Enable mysqlnd], no, no) - -PHP_ARG_WITH(openssl_dir, dir of openssl, -[ --with-openssl-dir[=DIR] Include OpenSSL support (requires OpenSSL >= 1.0.2)], no, no) - -PHP_ARG_WITH(jemalloc_dir, dir of jemalloc, -[ --with-jemalloc-dir[=DIR] Include jemalloc support], no, no) - -PHP_ARG_ENABLE(asan, enable asan, -[ --enable-asan Enable asan], no, no) - -PHP_ARG_ENABLE(swoole-coverage, whether to enable swoole coverage support, -[ --enable-swoole-coverage Enable swoole coverage support], no, no) - -PHP_ARG_ENABLE(swoole-dev, whether to enable Swoole developer build flags, -[ --enable-swoole-dev Enable developer flags], no, no) - -PHP_ARG_ENABLE(swoole-json, whether to enable Swoole JSON build flags, -[ --enable-swoole-json Enable JSON support], no, no) - -PHP_ARG_ENABLE(swoole-curl, whether to enable Swoole CURL build flags, -[ --enable-swoole-curl Enable cURL support], no, no) +PHP_ARG_ENABLE([debug-log], + [enable debug log], + [AS_HELP_STRING([--enable-debug-log], + [Enable swoole debug log])], [no], [no]) + +PHP_ARG_ENABLE([trace-log], + [enable trace log], + [AS_HELP_STRING([--enable-trace-log], + [Enable swoole trace log])], [no], [no]) + +PHP_ARG_ENABLE([sockets], + [enable sockets support], + [AS_HELP_STRING([--enable-sockets], + [Do you have sockets extension?])], [no], [no]) + +PHP_ARG_ENABLE([openssl], + [enable openssl support], + [AS_HELP_STRING([--enable-openssl], + [Use openssl])], [no], [no]) + +PHP_ARG_ENABLE([swoole], + [swoole support], + [AS_HELP_STRING([--enable-swoole], + [Enable swoole support])], [enable_swoole="yes"]) + +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_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], + [AS_HELP_STRING([--enable-asan], + [Enable asan])], [no], [no]) + +PHP_ARG_ENABLE([swoole-coverage], + [whether to enable swoole coverage support], + [AS_HELP_STRING([--enable-swoole-coverage], + [Enable swoole coverage support])], [no], [no]) + +PHP_ARG_ENABLE([swoole-dev], + [whether to enable Swoole developer build flags], + [AS_HELP_STRING([--enable-swoole-dev], + [Enable developer flags])], [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 @@ -94,6 +183,7 @@ AC_DEFUN([AC_SWOOLE_CPU_AFFINITY], #include typedef cpuset_t cpu_set_t; #else + #define _GNU_SOURCE 1 #include #endif ]], [[ @@ -146,6 +236,7 @@ AC_DEFUN([AC_SWOOLE_HAVE_UCONTEXT], [ AC_MSG_CHECKING([for ucontext]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #define _XOPEN_SOURCE #include #include #include @@ -163,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 ]], [[ @@ -174,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 ]], [[ @@ -190,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], [ @@ -275,9 +377,8 @@ AC_COMPILE_IFELSE([ ) AC_MSG_RESULT([$CLANG]) -if test "$CLANG" = "yes"; then - CFLAGS="$CFLAGS -std=gnu89" -fi +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 @@ -299,13 +400,15 @@ if test "$PHP_SWOOLE" != "no"; then 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])) AC_CHECK_LIB(pthread, pthread_barrier_init, AC_DEFINE(HAVE_PTHREAD_BARRIER, 1, [have pthread_barrier_init])) + 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])) if test "$PHP_SWOOLE_DEV" = "yes"; then AX_CHECK_COMPILE_FLAG(-Wbool-conversion, _MAINTAINER_CFLAGS="$_MAINTAINER_CFLAGS -Wbool-conversion") @@ -342,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] ) @@ -389,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]) @@ -410,6 +971,14 @@ if test "$PHP_SWOOLE" != "no"; 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]) + AC_DEFINE(HAVE_CARES, 1, [have c-ares]) + fi + AC_SWOOLE_CPU_AFFINITY AC_SWOOLE_HAVE_REUSEPORT AC_SWOOLE_HAVE_FUTEX @@ -423,159 +992,116 @@ if test "$PHP_SWOOLE" != "no"; then [cygwin*], [SW_OS="CYGWIN"], [mingw*], [SW_OS="MINGW"], [linux*], [SW_OS="LINUX"], + [*bsd*], [SW_OS="BSD"], [] ) 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_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 \ @@ -587,65 +1113,115 @@ 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/net.c \ - thirdparty/hiredis/read.c \ - thirdparty/hiredis/sds.c" + if test "$PHP_NGHTTP2_DIR" = "no"; then + swoole_source_file="$swoole_source_file \ + thirdparty/nghttp2/nghttp2_hd.c \ + thirdparty/nghttp2/nghttp2_rcbuf.c \ + thirdparty/nghttp2/nghttp2_helper.c \ + thirdparty/nghttp2/nghttp2_buf.c \ + thirdparty/nghttp2/nghttp2_mem.c \ + thirdparty/nghttp2/nghttp2_hd_huffman.c \ + thirdparty/nghttp2/nghttp2_hd_huffman_data.c" + fi - swoole_source_file="$swoole_source_file \ - thirdparty/nghttp2/nghttp2_hd.c \ - thirdparty/nghttp2/nghttp2_rcbuf.c \ - thirdparty/nghttp2/nghttp2_helper.c \ - thirdparty/nghttp2/nghttp2_buf.c \ - thirdparty/nghttp2/nghttp2_mem.c \ - thirdparty/nghttp2/nghttp2_hd_huffman.c \ - thirdparty/nghttp2/nghttp2_hd_huffman_data.c" + dnl During static compilation, there is no php-config variable, + dnl but the php-version variable is always present and is not affected by the shell environment variables. + dnl During dynamic compilation, the php-config variable is always available, whereas the php-version variable is absent. + + if test -z "$PHP_CONFIG"; then + if test -z "$PHP_VERSION"; then + AC_MSG_ERROR([the PHP_VERSION variable must be defined]) + else + SW_PHP_VERSION=$PHP_VERSION + fi + else + SW_PHP_VERSION=`$PHP_CONFIG --version` + fi + + SW_PHP_VERSION_ID=`echo "${SW_PHP_VERSION}" | $AWK 'BEGIN { FS = "."; } { printf "%d", ([$]1 * 10 + [$]2); }'` + + if test "$SW_PHP_VERSION_ID" = "82"; then + SW_PHP_THIRDPARTY_DIR="thirdparty/php81" + else + SW_PHP_THIRDPARTY_DIR="thirdparty/php${SW_PHP_VERSION_ID}" + fi + + AC_MSG_NOTICE([php version: $SW_PHP_VERSION, version_id: $SW_PHP_VERSION_ID, thirdparty_dir: $SW_PHP_THIRDPARTY_DIR]) + + if test "$PHP_SWOOLE_PGSQL" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_statement.c" + if test "$SW_PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_pgsql/pgsql_sql_parser.c" + fi + fi + + if test "$PHP_SWOOLE_ORACLE" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_oci/oci_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_oci/oci_statement.c" + fi + + if test "$PHP_SWOOLE_ODBC" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_odbc/odbc_stmt.c" + fi + + if test "$PHP_SWOOLE_SQLITE" != "no"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_driver.c \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_statement.c" + if test "$SW_PHP_VERSION_ID" -ge "84"; then + swoole_source_file="$swoole_source_file \ + ${SW_PHP_THIRDPARTY_DIR}/pdo_sqlite/sqlite_sql_parser.c" + fi + fi SW_ASM_DIR="thirdparty/boost/asm/" SW_USE_ASM_CONTEXT="yes" AS_CASE([$host_cpu], [x86_64*], [SW_CPU="x86_64"], + [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" ] ) if test "$SW_OS" = "MAC"; then - if test "$SW_CPU" = "arm"; then - SW_CONTEXT_ASM_FILE="arm_aapcs_macho_gas.S" - elif test "$SW_CPU" = "arm64"; then - SW_CONTEXT_ASM_FILE="arm64_aapcs_macho_gas.S" - else - SW_CONTEXT_ASM_FILE="combined_sysv_macho_gas.S" - fi + SW_CONTEXT_ASM_FILE="combined_sysv_macho_gas.S" elif test "$SW_CPU" = "x86_64"; then - if test "$SW_OS" = "LINUX"; then + if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="x86_64_sysv_elf_gas.S" else SW_USE_ASM_CONTEXT="no" fi elif test "$SW_CPU" = "x86"; then - if test "$SW_OS" = "LINUX"; then + if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="i386_sysv_elf_gas.S" else SW_USE_ASM_CONTEXT="no" fi - elif test "$SW_CPU" = "arm"; then - if test "$SW_OS" = "LINUX"; 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 SW_USE_ASM_CONTEXT="no" fi elif test "$SW_CPU" = "arm64"; then - if test "$SW_OS" = "LINUX"; then + if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="arm64_aapcs_elf_gas.S" else SW_USE_ASM_CONTEXT="no" @@ -657,21 +1233,44 @@ if test "$PHP_SWOOLE" != "no"; then SW_USE_ASM_CONTEXT="no" fi elif test "$SW_CPU" = "ppc64"; then - if test "$SW_OS" = "LINUX"; then + if test "$SW_OS" = "LINUX" || test "$SW_OS" = "BSD"; then SW_CONTEXT_ASM_FILE="ppc64_sysv_elf_gas.S" 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" + fi + if test "$SW_USE_ASM_CONTEXT" = "yes"; then swoole_source_file="$swoole_source_file \ ${SW_ASM_DIR}make_${SW_CONTEXT_ASM_FILE} \ @@ -679,12 +1278,14 @@ if test "$PHP_SWOOLE" != "no"; then AC_DEFINE(SW_USE_ASM_CONTEXT, 1, [use boost asm context]) fi - PHP_NEW_EXTENSION(swoole, $swoole_source_file, $ext_shared,,$EXTRA_CFLAGS, 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 @@ -695,7 +1296,11 @@ if test "$PHP_SWOOLE" != "no"; then AC_MSG_RESULT([disabled]) fi - PHP_INSTALL_HEADERS([ext/swoole], [ext-src/*.h config.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() @@ -707,6 +1312,10 @@ if test "$PHP_SWOOLE" != "no"; then CXXFLAGS="$CXXFLAGS -std=c++11" fi + if test "$SW_CPU" = "arm"; then + PHP_ADD_LIBRARY(atomic, 1, SWOOLE_SHARED_LIBADD) + fi + PHP_ADD_BUILD_DIR($ext_builddir/ext-src) PHP_ADD_BUILD_DIR($ext_builddir/src/core) PHP_ADD_BUILD_DIR($ext_builddir/src/memory) @@ -720,9 +1329,31 @@ if test "$PHP_SWOOLE" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/src/wrapper) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/boost/asm) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/hiredis) - PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/sockets) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/standard) PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php/curl) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/curl) + if test "$PHP_NGHTTP2_DIR" = "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/nghttp2) + fi + if test "$PHP_SWOOLE_PGSQL" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_pgsql) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_pgsql) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_pgsql) + fi + if test "$PHP_SWOOLE_ODBC" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_odbc) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_odbc) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_odbc) + fi + if test "$PHP_SWOOLE_ORACLE" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_oci) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_oci) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_oci) + fi + if test "$PHP_SWOOLE_SQLITE" != "no"; then + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php81/pdo_sqlite) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php83/pdo_sqlite) + PHP_ADD_BUILD_DIR($ext_builddir/thirdparty/php84/pdo_sqlite) + fi fi diff --git a/core-tests/CMakeLists.txt b/core-tests/CMakeLists.txt index b8f6478ed6a..807f5337cf6 100755 --- a/core-tests/CMakeLists.txt +++ b/core-tests/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.1) +cmake_minimum_required(VERSION 2.8.12) project(core_tests) @@ -10,14 +10,14 @@ file(GLOB_RECURSE SOURCE_FILES FOLLOW_SYMLINKS src/*.cpp deps/llhttp/src/*.c) add_definitions(-DHAVE_CONFIG_H) -set(core_tests_includes ./include/ ../thirdparty/hiredis ./deps/llhttp/include) +set(core_tests_includes ./include/ ../thirdparty ../thirdparty/hiredis ./deps/llhttp/include /usr/local/include) set(core_tests_libraries) set(core_tests_link_directories /usr/local/lib) -list(APPEND core_tests_libraries pthread) +list(APPEND core_tests_libraries pthread gtest gtest_main) # find GTest -find_package(GTest) +find_package(GTest REQUIRED) if (!${GTEST_FOUND}) message(FATAL_ERROR "Not found GTest") endif() @@ -61,6 +61,10 @@ if (DEFINED enable_asan) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") endif() +if (DEFINED enable_thread) + add_definitions(-DSW_THREAD) +endif() + # should execute before the add_executable command link_directories(${core_tests_link_directories}) diff --git a/core-tests/README-CN.md b/core-tests/README-CN.md new file mode 100644 index 00000000000..46d12ec9a0c --- /dev/null +++ b/core-tests/README-CN.md @@ -0,0 +1,50 @@ +**Swoole 核心单元测试** +=========== +## **1. 编译googletest** +swoole单元测试依赖于googletest,因此第一步我们需要编译googletest。 + +这里要求gcc和和g++编译器的版本要大于4.8.5。 +```shell +wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz +tar zxf release-1.11.0.tar.gz +cd googletest-release-1.11.0 +mkdir ./build && cd ./build +cmake .. +make -j +make install +``` +如果你已经安装了更高版本的gcc和g++编译器,但是在执行cmake . 的过程中还是发现如下输出。 +```shell +- The C compiler identification is GNU 4.8.5 +- The CXX compiler identification is GNU 4.8.5 +``` +请执行下面的代码并重试。 +```shell +export CC=/usr/local/bin/gcc +export CXX=/usr/local/bin/g++ +``` + +## **2. 编译swoole.so** +```shell +export SWOOLE_DIR=/your-path/swoole-src/ +git clone https://github.com/swoole/swoole-src.git +cd /your-path/swoole-src/ +./make.sh cmake +``` + +## **3. 拉取基础镜像** +```shell +docker pull vimagick/tinyproxy +docker create --name vimagicktinyproxy -p 8888:8888 vimagick/tinyproxy +docker start vimagicktinyproxy + +docker pull xkuma/socks5 +docker create --name xkumasocks5 -p 1080:1080 -e "PROXY_USER=user" -e "PROXY_PASSWORD=password" -e "PROXY_SERVER=0.0.0.0:1080" xkuma/socks5 +docker start xkumasocks5 +``` + +## **4. 运行swoole单元测试** +```shell +cd /your-path/swoole-src/core-tests +./run.sh +``` diff --git a/core-tests/README.md b/core-tests/README.md new file mode 100644 index 00000000000..2f3c6644b9b --- /dev/null +++ b/core-tests/README.md @@ -0,0 +1,54 @@ +**Swoole core unit testing** +=========== + +> All test files have the `.cpp` extension + +## **1. Compiling googletest** +Since swoole core unit testing rely on googletest, we need compile googletest at first. + +gcc compiler version > 4.8.5 and gcc-c++ compiler version > 4.8.5. +```shell +wget https://github.com/google/googletest/archive/refs/tags/release-1.11.0.tar.gz +tar zxf release-1.11.0.tar.gz +cd googletest-release-1.11.0 +mkdir ./build && cd ./build +cmake .. +make -j +make install +``` +The output still contains the following messages after compiling higher version gcc and gcc-c++. + +```shell +- The C compiler identification is GNU 4.8.5 +- The CXX compiler identification is GNU 4.8.5 +``` +Please execute following code and retry. +```shell +export CC=/usr/local/bin/gcc +export CXX=/usr/local/bin/g++ +``` + +## **2. How to compile swoole.so** +```shell +export SWOOLE_DIR=/your-path/swoole-src/ +git clone https://github.com/swoole/swoole-src.git +cd /your-path/swoole-src/ +./make.sh cmake +``` + +## **3. Pulling images for testing** +```shell +docker pull vimagick/tinyproxy +docker create --name vimagicktinyproxy -p 8888:8888 vimagick/tinyproxy +docker start vimagicktinyproxy + +docker pull xkuma/socks5 +docker create --name xkumasocks5 -p 1080:1080 -e "PROXY_USER=user" -e "PROXY_PASSWORD=password" -e "PROXY_SERVER=0.0.0.0:1080" xkuma/socks5 +docker start xkumasocks5 +``` + +## **4. Run swoole core testing** +```shell +cd /your-path/swoole-src/core-tests +./run.sh +``` diff --git a/core-tests/docker-compose.yml b/core-tests/docker-compose.yml new file mode 100755 index 00000000000..4188b9de5f6 --- /dev/null +++ b/core-tests/docker-compose.yml @@ -0,0 +1,18 @@ +version: '3.4' +services: + httpbin: + container_name: "httpbin" + image: "kennethreitz/httpbin" + tinyproxy: + container_name: "tinyproxy" + image: "vimagick/tinyproxy" + socks5: + container_name: "socks5" + image: "xkuma/socks5" + ports: + - "8080:1080" + environment: + - PROXY_USER=user + - PROXY_PASSWORD=password + - PROXY_SERVER=0.0.0.0:1080 + diff --git a/core-tests/fuzz/cases/req1.bin b/core-tests/fuzz/cases/req1.bin new file mode 100644 index 00000000000..fc9b786372c --- /dev/null +++ b/core-tests/fuzz/cases/req1.bin @@ -0,0 +1,19 @@ +POST / HTTP/1.1 +Host: 127.0.0.1:35347 +Accept: */* +Content-Length: 11199 +Content-Type: multipart/form-data; boundary=------------------------18610972117ffaf2 + +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="test" + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="hello" + +ibOnkVsuKvLnzrTeS81ueAmswSWiUUvcOtHAamPBw2NIgEM3286PmK0lvCcPDnsYQ0lm8/emwynbcngwmUJ5983lmEBj49th5ED73eFUGHSLQzTY1nAaD54f +--------------------------18610972117ffaf2 +Content-Disposition: form-data; name="world" + +NDdKoaAEmCjD+MFAo1tOBTJ0yPTWG2amx0f8wI/PJf/sIB+XAxRmdftsyd48o47Lt8U8pgIxBd4xcUsLGVomZZT13eIpsCHnPjSdgAr8vSBrxFciyHBIzkhFX8IxTBFs+YqMqiH5/YWx/TQnxoqYWcqa86RShKne2JmF6+JXZUkB6O3I21X1cioUmdK9d3pOBWGUviL9oY99FA0fVAC9qH01hyO2AQyh3hjhvpq1uPYhjnCwetQlpYExhxPC2Q1gJ9edA8pmccWFSRlS079wq8l1ikkQqNnLvzp15pzNmUbvDGXAywsQrpJco3G9b8gwljHGUPpctxBB8DyO1LfavJ8Zusa9rSA4NAA3NzZpZ5bbKy5rYFQASuFXd7orlXdoPkyZ7ERSqgIT6y8Y3pnPrzAs8LLEmGuWbjN6ME7PtUuT87f10GtobckOUqYJktX9fmD4lxgJT0uUam++HG1j3ztFzyaY1pCREYmKewy2Q5FTgYQxKNzN4WdRmz+X8k2upRFDEr22Gm1wUEct2vOZDKhp0XBtV20uGKg0kIb4OM8RU5b/vymTss5+eHX5G/zy09+fXyzs7RpUls/yavSiZJT6iYXciBINqfYcmG6f3arBdP3sgR59UhLnWn3wfNeKyYshiy1HcKDvOEZea/A81XSULo4iVmlYghACFrwvkyHU6oaV/chWID0IsNWtylIo5OaDFDuZLGm3YzQ58ew21dQZ+Nifl2QydeGBO1siZt9mbcIAXqIylvP3iT1CVab7PCiGPXNtYntLa7581Zceg73i4g1ZzuaWULUqkF5WPLWroTCWzDl+/2WsU3+WAIOON3dRTz/DirZG7e0UxmaseAT+xoDyVMIZK0fuaoc3s0ozivKE4bItfYMMhkyRQpDIjzTKB3YHBCH9aWnVcxLRnVF6qt6hO/bZfh/isskDbiCoBD8YcSPWgN/qLYDBOJFy8us9TdiWPZdBebdHmqR889s4Mow3TYcrbmaVmnwwPqSWgsfhKiLoaK2MKWgSxVlGGVQuB8Odw/YrAvFAEANKNv0D92pUfWBY8Rolh6MMECiYTuHLeGBRENx/1TWaZ1ASP4hfp/fP0ylTSimfxexOEHKDJGlQdsSgQNt7uXYlFuwhRVu78FacQcLhSDMAJT9oXmmy9/BX4x8F68IkV9A2XYEfa4G6KzM4reMyccXIM2GnBKkoQgfwcPNuRwo/riyqQyMbf03MINJ/IwwmI1lnXRL6Uzmn3zUV9aurDrhRTrVfqAhPqEJLrqsPduI2DXQWvBXNiWQctK5pTCWnV9qj9qy25CBggvP8c749mlPPhTmrFt2Nk2QeS7t+oiq/E+G1ir0daEk+3M3oqEVRMbcnkftu+d2aMPuBSDwt6l7nV+GCQ89DXp20enwbeUU/JVLRGYkqpj+17Tw/8YxxYTTkVbNNotv8tOKqmwyN0XzRffQ03aVbNhjkUgESQahE05swWxRXrNmiUpIBFT/yMSSoqGl5aTbDtBjNPSpX7oSCCBNdoT3MnVdPCbPAMW4PxenhPUKWLLOVtFztMnFsl4/IIMtUjkBbt3cONJCIsNuXzSvfsEso9CYjH9VUWyDcZj614Ov8KIEYJZkJsF1nlxCJyChQedCDzLZNCR5sJY/R9dv5A88ixB1GhRSd5qjWpjnXTAm8YLVtDWzH9v0ACVNsq4zYYUQioJk6ck13meDgBEu4etqqyeVUdbJAkHo2R4MAo4uJ2/sMQL5xniZ5WRYTBP4JFJxkz7qNIzn8n+7XxuotnfvqShCuUC/Xt/SZFHD2INMBG2u4OLlARBWj7TPkLsQwczaGyXC9j85mbXvOpxbqlBAtADQlIOfqaeROahEkNKYnmCb09PF1Sss3l4BKnYaVShO+pZ7MnJSR0Mpx6R5uV0dGW/VXmeOSrSCouDkunAnX1jMEEje35szpDkqgMaYqlaoOkMRrhCZwL7YhQjSBe1GnFDHGvQxX8i3AnXfgzA+9AAiITOv7k0h/PxgGNUHlaBkWyaGIi3pS0Dz/4yGmP3BYr0H/qHpeXte1kd/s8JRxyILGDgC51Y+ExYvjZL63PsmmPNfHyY7LDTX/UfTweISeF7q1b8HuoQuwxvVmq6l/C+/ewDXVeort1tbDED+h1dCDqOEBXGupZu2uKHfWICgdLOHzPEnzyNsykqCAAlKTtFp6VAwLo3b3xWFSvhXfk4Yi/23+rZc3eL2JFwaHcULVkCr6AzOwriALLBBU2RT2qj1cAwqWNQQUbH7tHViuBxBT8/B9MjcU9HQHbdKSvEocs/4fqgMJfvj2YD4O7qGzLaBghkfKS1HAOyt3MNf5q4k7lQndHi0U8cAYMo6aAuxYOccxOzobU2uKiyZUx0WpH3ZE+Ge8W7Hc7bMSoH5ISNKTti8tIzNmt+CyIRPVzSjiodhaUr82ErCKq3UJTe3Kr+JAWkthB5BtcZWFVqMT7yb6cwtzoOiJKc+WaPX0rnwiHLJTQEhOGR/4Jh1YI39M8kpZG0HPPJbiK3bZuDgVyRgLsGm17vBpTv+dp93Kp3wquuATyGloMu7/GnjSrXzTOcVdkW0F5qMI+QSG0eK3130ndIw9Qt+Ir/UtYvqcTDCdrX5LJrJ3VLKSGqr5MPc8uHHhjZvWUeUtR8Dt7b9Qy8M7UZ+fWcRzVHMnqFzxbsOTkI9Ck1jlBe4p/Qks8EZ74zmc3SL2ZH672U9CQXhOLzyd/E435m60Pz5yqXjq/ZYdTl566IEoOA1YoqbbA+A2Co11Xjp3P2Mj3ns82ubjp9vkIkJboe9O4WghDbcPSWyiyioIUhp7mSrhgrZn8khgY0akKuRplrD58dhH0f+QCbk4/ovPjKLFwoiksfsvfQrO/5wSLAKQA+5rycB4JQj4gkDtKxW5/I2Mf0y/Js6gxAFMqCcRykc9Gm7WGRMmRDefluPLlajxOdj6jbA20RH23ydw4E5a0cRPYiZXlssEbrzu7qsdfisG4jmLoApu1PyokcshUWzkvJnevFg3LIgW9Xl3hKLyGLXQQR8f+ngVbP/rBkhY7R3lramIo7npGsCKQlv41/4Z6Vk+JTKd1mfvXq6mYJeeYHCAFw+BP8KcXFRav3J0XJccb69wuziZtu/h3KCOqL7q9mN0Y7ftOR+l+hJ+k+v/T0fbvHkwlBsI/I15f5r8S6KK3pnBcL14RD3dluZlr9GuR9hJtGc6GKmkV+QqPph7tPvSqtOaYI4CYrbHegBcWOirj7BEF0qGuNV6hYOxkZa6cSQP2p2rPTvzv3L2usPbCLDlgN7Yu+RJ0icpFCpkZE0V6w0sbr0ZBvYO8eaFu8TVwT84cXD2A8XSSCMm5w5HCQpIThfQzvW+xWTW7nO5y9LEPUQbh+fZMs+KABuEM5zIXyavqMdSumlWyX51FhAqhb4ys5LqQSrKM0u+qxUcweXOt2lsK1WgibFVIhuPzuCYo+hVFHNXQpxjpvX89N4Oxjg3jb3OWHOUqZsGU23aWLpOP8Vpmh4RxYZE6k07C16DZwHbe86tbSLrt6ezzjcmwZbtGwHd8iTi2sLjVZGplYENTw7Mglgr33ImoOD1MRjfWzTr2xwsfOzR+emSOnL3PBB1Xh6usXsyop/yeJQfJN4J+epduKW/2V6Egzc7t4Klf3wBtjysoFnTHiQ9u9IY81F0YjfHWUcAETiAZoGkl1LNCxRxa5a7zKHapEzu/xslNjnsXtNI7bghsUix3biyHDjU+eEBsO968R7haIoe3l/qF+/15EJKB0zCbiz/+muAU9/Q6Y6NZFWmKVz4TibFS7GdxMmaB5kM2Z3fEkOv27UqUk0p9J3n0lD9y14Zw+C4zyd15Ue2WVZVVAqwysjqAIPMfhDaV2f3Tvt6H6Ft8zolb1pjIl/EH09jUW2cRg7B3Q97qGFA0NVJ9CKDTbCwETEBiqnxiDrvHc2Pa4W19ZCIR3Ne3LorlaXLjMGSEmOmWBB10YH58FyIupUCCOLsK5PcBQoD+KoeVXkF4IhZJtajdlzpH4AmKP9MfxTZvQZO0vtixzYDyJ3PlNBa8zDKvJoJ01PIqZ1Z+AVo0pzesQ42Hz8Ch1V8wouwFN97Ah8QZzPJdR+kUcuBbqJ6u8WWZM3ippzpVdjQg5QwzjDxWxmgqgnTFXyW00yqNPQQDxi0bCXRD8x2f8P0Weh+NeWwajz4lXY310FfqWsCP07UsnsYxIk3vG1n5U37pJJZiJb3m07zH/HTwELeobIzYs2292tqW6yDSH8IZEHyBnvdtcIJ9ZrykG3NLjWByTA4w0j+VaiamfHmUIzY13q/PxCJxZUn8VnlZFJHXrI7iTO+FWKD39EZsKflsqslJZfFfVGGaJlPehq4nkiV4/YYP4Yi92DIkjCHiXXO4BlxWJbX+nPX1NF2B4ImHOThZM5Qr1yjv0oGKtxIx0zGuBLYiOWMTPGGLV4O+DBOkPm2XK3BUfoHjEIdAVprwqWK+4iah8XWmOrWuyk41OlvAd51h3LZRvRnkw+skhB+GOYDeQHKcmtSH645Hl2SU/abghLs8owaCPGorTDXNIBxbhoAFCRXVl5hMzA0hXnqWzWHHNpq3BKXGRCSovuYEjPlXM4KRR6n+cNzhWqXdpCkaNpXz8sy6rw43BM98Tf6djTdYR6QN+YYSC4/CD+ddxtmFI0l/tsNb5VU8fntbDQx5PS4Y7J37PbKvKS1gNCQo0Rlyc3GteFTKl/eg6nJMed86KaUYgO7I2XL8zzQ7C8ssgh6z07U9TyIHVw/ld80/B6UjwJdzu+o2izrQtOotgO1kZ2R6hhP5gA6z23UQ8RcOLPmOcHbq25kNGTsLmMLM6beFUQfn+W3TsWi0Nz5SLm+qOCbc/LEPNV0Y1ZcS1XwLHpr/feeflmOjc02/NCJrOxtRh4u/xPQtNQ90nGGXnajzaHY8Cmtl1bSBQ5ldfBevc1F0jGQeXQh26wwB7O/E6ypDyQw3glxzn2whATTBUGRNb5aM1b8QG7HwGDapucIGlc7EvGqnIOHKWZ7yYhmfZzAgW45pdtOybKQrzmrwIjngD/9z2a4BduwJwR40kS8n3am0Ao7UcLSv1LWL1Yn4rn8TiGhsBMOuGtepECOABBhqkB8Z0YXxgf0tUytSapuFX2NV45QXocoxd4xoLcyNeq1dWyTdlgqOzkvdsjUlMDyccmHUC0uQGaVnt7tV2D9lOZW0YgGcHFJN/MfhnVuoYLyiX+tJDsXXOvA5wMuU+jawoJ4a1MTiUBVLzS5W1iyO56od06nJhmM5ld0coIGW9bal//LSM2jG3hqeHjggy+QoDyCO+3gCYGAP7spmyb+UA0bjLnUROEKp843kx87iRnFb31dzX3PhvYEO5mRFnbAFpsxp+w99Z1Aspc0cJn0+4hTNTZ3ffRWmNSfhKkuitqeIUSLfIyZIQ5qbgeisH0bl783wm4f/1NKB/x0fWbrs3JOag+7BN3YJeihEx+YG8SQlyrNiZ8eBV8WLPl6BbML8ap5fxEwghKQkBVBnPNkRRxVSvT1bQetqWgtJHd0GpTyLp9oyMuOeSAobxk+dV0c+BXxD1WIuy/2pa7lOEiYTTVjlsXVI2QS6maV3dxf1T9gZHFaLPBKnHajQErCD71zpEHiByCRUQ0oO9bMvCpRyi8SYZX4mxFiT2KenJpqWHc6SPGpz1fq/RzzSAf3hhgRSYGZjhg+MLDIh3QhgB2oxE7aoNR5Wi2BpkPlh3cpg+KJXNjKs5DX6csBu1Ooeprc9jclolszA3jdFiNCI3vcCfLkHiZ0nmtAin9UzinLss/M2Dfzt6CZKM5s0JrNfpOxMIV+nOGR8YU0+BlkEych3vFqtQ3ETKpVNCesMGzYIAbsn4lehYv97lbneHJP6/zMlenEqF4etlaE/OkORz2LVcmPtfMYdetcWK+xZN/syzSYvYfmNTGJNppvhuKHnviAaL3TBx3PlTAFm3waadzWBitNzSVjdqtnNmgiOWaHYK6j3LosuigFnoqb0XtxP4zXMcruF/ikg1tFa4R0BSPXXikibnbqU7odFPT0jfY4KQn37rYu9U8iYqfmni7l/fu/MNH9qf7e/AQaKWL9cJATn48iIqLtT1OX4SEuPJUG1gYaQgZx5lVAQIKjI64hIq36c7syBGP1rlzmy7NKEbuE3kvQV0qOm2VUTA6u2lRxBTZfJu0VVJViVRdY1Jxw9aX/SFR1A2peGTXv4L7IdnGdRS971EkroEm/LiYibySrxxKhIMgr34jKa1p7mgjuGlPk7mvGkY079zlWsPr0UypSTQeMMThqNNZiycPQBjuMEkj2TxF11+5X1V+Xg/Bef/o9hYR0cOFh+zIjyd5O+Wj+dSfhEiBlFX5mdR+Av+00Ryvs0ePrDAOs2gfBGNqWpROMMwILTc6e4VgYn6+f4g8DYJzuS8UnlvIq1aH9W/UB+x9gH7tDogA9rIHn/XRSNiuJJA7Sqr2O1aB+iA/AokQFwISfi9Sjf57ih844QES0G9yDgTOCiylpkrR/FYtZcB2LlocbQvydmZwxoXpnHER8x/SW3YG85i6e1E2utK0MWN+WQs3QrHtttdfnp/j7QjKJpsLT2dS24MCgj7CDNJcY0SXyDWs4rmVj50zvzZhs3W+jNrvDIsh3FRGSu/fXiezJWIb4m3gevc8lomm5hzar1eBKSnNm5yZDU81HBKtxeDjmGLQ7jmxaqLb3+DvIs4v9HWYqs0uZ2CU1MA79FCAnX1LVJKzYROxAvhQPsukZ+tk38lClzkPryN7HGHR0xF0+UP9wVX9hK5Ss1CX+2lGTU0XkE8kufXTYh1R/Xque9AORgwT9IaumMnlPC9K1i64txCbHHmRgYtNcnwlbVMYy7UFH1Bc7IqYKQnu2l6cNoococsfwWJ6ciAJ+w8hMs2/ILnFvfvQunYVdEqu35GLlTZVF8O/+j9VydwwwzpEynZG6AzoKyP8GlPhQ8V/CcYXVBkEFMq4qUgvHhjoHRjuUom0IAx6InT4H8dG36UvslmzL2cNwP0NEDMu59lEGBAaa7RCjvR7frUMjjEXkcnXZH3KDPy/dwN5UX7FyghMdM4GxfSy7p79+ccJhjtTPoXrcVsMJdEntXhznJoSt51r3ZgeJNpwtrK/5d1jlLd5VGQDzH6UzH2gYxllNLjjoxaMNT30eGJ0nzaVWzn0X2JwLug1ZMOFPqyMDfrl94LzuxNWOUCw9cJ9pbqVaPmIzFM7o88O7BYJ5M90vgYqQvaTz0a2poECEoah/z2bxYrmDSIYiWnJ5TDFy3sOV2OHn8LLMS5JTV1bJh1uaht9pbjJjj1Rp9FetVUwXixiDNbSYPFUhkl3SXFeYTf5YWEpp6sVbMSKatWTUenQ4qESuZN+lKDSrOeIN1PheQkLwEAgIJYvMQkB23khDTqvMnVHahNU4OKZn+Xs+iyWa42/K2neqT+D4IOTAfUUXX0HGQJI0+8KE5NZrEMB+bB7mKRTNzW7Rg1aRmrgZoLgnb3hIft5VMrgebQSQ5UY0YwTNXg4fko+TlKoyEbO8PrYpawQ0vJOMt3T7lELcG+mWqHla7ba0o997l6KGrKcDT/cEe4z/hU5AQi391v+WfwSjxaMQewxCwh93+rUqqMXCWjVAi8SoxH514jVlMz1ukEXPMxiiwxb4+feME5aYN3pqdoPEccJZzEQpYQy9z02hLdTLMnrT3But8n1JjoT4AwTqBmzHlxPMSoWJvXKQ3nkUVb6BKhp3EM+kOd1kSw8HsRG/aWeZtTdm+CEOqX7YQYm47QKNOb3xS3dhzv4JsDvM/kkkfwYN/h/hOjbSZXUwdgvFtVznSu//3I5rxg6g6kDew1H8Y4Ax0yMhPSwycXtHqfbBptXOFfhvoJoYiz1w0nw5DhjFlLqMjBvj0mbWWpzIikpbl4cSpmmX2UP/HTfsf5BEull4YmY6/wNIc8jRlJbaENYf30De9ENhBjQRPDsjSXyeJb/2sa0Izb00G+bZtF63iRtPRgiBr2rJd1BDKOYSZcAMbB56o1dUwOc755Nhww/FGU9do3vpbFKgaFME8GXngdguC7+fh8DaFwe0B3J0iAhSQ9wX9VrU+XWsUCrK6Zlo2/33i0+5bbzprIhVBbnw78N1efYLOmEEkZvnRVXMyXmTapgDRmOVSuaK3vH5RKhFl28oUd4EQNALQ9r3vtAVr9sLkL55neaBAbGJ1HJeLATcgr5IcXkg3oI8mP2qDrWjD3NtQfezYej7uYslASEHMWeZ4EwyJDAVYaV1y6XPv10I6AFzT1iTwEnG614Ky9goFJDimDcIpnaDxZIe0A81kNF20y5ia7M3ycJK/G5QwvVxvy3qh4tDQKBn7GBbjFXmeVRasZtGiMgcyn4DpO6vABa3OMc+BAE8lekI8qdEDxo2c5ef09y63gL8TELxHfMEHoqUfDebn1miYLGVax5yqkvKbGME67SBJ2Y2GvFsfckeOU59GfzuVIpimjN+mqHy7FVryZVmdxSp4Klqdoq50ef0GstffYrw7Hb5HKRL/Hnjssxs+MqI0+b1M6DABP8WGsmFyU+7aToPMZGDmbEqr2K7/rCCrGwyYAcYOLjrT4sgGLeiNE9omcgYLPRYX0IV2QyKb7TmxCLQfB/J5Ea0MwIjqQZGZNzsCM8ox1TR+P5n/TQM+H+zk3miFzsbWRz6YYcLCKrGIr/jwXH++fsDl7bPWOakRR7xVnJs4/h260OvB9Q/xf/0QdeNMZK3XlK7n/gnFUsWFJCYISyLmVnl1qglxu4ohqS9r16Yra4HcqozrJfP6BWh/URq8lK8a1ssw7i3erS8umy4PZcrlI7H6YArt1v3K7tp3eQWhU0AngaQ6tguLoWTBzk46qBC4FV04suKlw0Prz0rOd0RM5g3ozm1NbbFvjUFBdRJDLBDaLpJRJajS6HpFZpNPaC7wQxBvOX1uMjuqfFB55VYT9sTOhIG1Rh5E8MiLulPFpotx6AkwgjnrRVhnd/plrSn8JRIXkXV+h4T69R7k+xHvOq6cJUwOlxyQXlgmM3M8UgYMSTRjjHhVPKF8ex3ikm2GPqEpsaaDn0RdvHAYja8333e2Ug8Q/1umXGi2XaOE1pIIWDF7Qry78tpAn1Mi2Xn3Ylo1AJXRRrj9AeJCMBNikTXEDvIwK5E+/luwOELnE1YslkQrZGPzAEoM+5GsqHZOpvI6F3dykob9wHIwrO6TShpcOlAc6LPqVmXYLXLMgnHCmE6/Cbl9000A6sXRmfNxIk/3+Eo3eY2WqW+YVt9XSWYQ56+/NV2HHVSMsGObNFZEGrUGyzc5L5+OrQVG0dVe/zGE5e0hERk0qj0l3JWapFIbzfm5Pw+qtg4IC6ylw0qiLma5XiDFQoxLMyCMXGEb0PbAdx02wZpxGK5LPsJf0falMrQOO6NSvDNb6+SBNvv2PoT5n1Vf9i7k8bwQ8ZR0FK34fA2WtlktA9ngh72lQcrxH+3wJ3sHUrqd1c0cgWNvEtmtRBS0dC7AR6Eveh/X4kHLQadOQDga8SD+WXyNPnlAzPoHmYU9zzJ9cM+9AXtKQ95kbm4iJFxu2i5ajv8LMgECo8CnnVt8ftW8FPhnYDeYtDSVY4A+aZd+Wmsr+SLzeFs8/R28VbUkgsL36/nLoGS+0s+SG+OSFWKtYSncPitI87m22Hsz4ZY+1Jynf+FbMoDrP7uDf5UGCaZIynsMq8rwaZzzQWNPK7aP1Za/yKRr2PhOTHHXFoQuwPlNPx1FO0UHKquzNirkhjRCApk+sHFsY9mkUahC33aXnQ08YEITCloGZQb+FIL5cE7NhqDa+UvDyWOBLQ6zZeHwQXrs3NY6RgnQ5mCJiIXSA45SJ1gEnlYvnGX9bj+z5et0QX0wflcQayV/B+sC48M59QKesGGAPhsH5wCRXAj7GsCQuAJjn5l8M8zx95dQoQf3wfltMOSh+mE6jy8q4ukof7dvlOeOD9unPKx56BSKFeX59T3pEM9gYMR9EN4IN7U5XGWbEi+6EfcTfUxgfNboFPykakTzku3uUABUpo3/8UMAsydGVg1cudDrBF+RnZT2vgK5i/1w5O9FPZvonXUq+5nHRJVUYn33GyccNkjrs+Y+yt08+QVDy+6wsX1SPOtEUaE59cpEdsjhbguOTn7/Kb6IbfeCnVChk6sBTLsvh7D4LUOQtbkzxhz+XCGFeRhvp4l3pBwmP8dv4PDROrAqp5UBIhKaJcZvvAhSwY2cbo+CNUYCcwrjWqjOwyEmedd2kCJp4s2b9bewldPHan5WX6AMecl/4NXp1sQrHPjO9en0Xz5n92qKpdZQC8p2AdSlASNhAAKBu6nBzlMbzoyex/vlJcWtAzMyleHdvHOHpFF0z0SF4kmkpGMz/WKxpH2cagE973ExU1ClJi/BIcjP+p203Z/YBTkZCFhy2GH8DK4jXWvPJIfkG5m5Z1xbrkNwJIfUfd90ubiYgbhxawEbKRjhFDtl7PV1wA8fHKOxCY6I0DN0mqxIDYgxc0FVE85+NSYR8z5ucjFCHQmCoSwWohznWvIANmVgDt5tu6HeCirL02uGNgmvpStMxosgYKU4Xt84XstQm19eQnJXTiceTogNzh3X3UWxELUnc46UC9NiMOZrOq/vKRgRUvW1JhwBhpmYOOyOdn8EqsJyAsjgFWNZE3gwi3RIaA6MIjkO6yCtJPs2g== +--------------------------18610972117ffaf2-- diff --git a/core-tests/fuzz/cases/req2.bin b/core-tests/fuzz/cases/req2.bin new file mode 100644 index 00000000000..7c0015fa2b0 Binary files /dev/null and b/core-tests/fuzz/cases/req2.bin differ diff --git a/core-tests/fuzz/project.json b/core-tests/fuzz/project.json new file mode 100644 index 00000000000..161f3f2dbf1 --- /dev/null +++ b/core-tests/fuzz/project.json @@ -0,0 +1,16 @@ +{ + "project": { + "name": "fuzz", + "type": "bin" + }, + "build": { + "ldflags": "", + "cxxflags": "", + "cflags": "", + "c_std": "", + "cxx_std": "" + }, + "install": { + "target": "" + } +} \ No newline at end of file diff --git a/core-tests/fuzz/src/main.cpp b/core-tests/fuzz/src/main.cpp new file mode 100644 index 00000000000..d945a40e675 --- /dev/null +++ b/core-tests/fuzz/src/main.cpp @@ -0,0 +1,48 @@ +#include "phpx_embed.h" +#include +#include +#include +#include + +using namespace php; +using namespace std; + +int main(int argc, char * argv[]) +{ + VM vm(argc, argv); + cout << "hello world" << endl; + + char buf[8192]; + ssize_t n; + + int fd = 0; + if (argc > 0) { + fd = open(argv[1], O_RDONLY); + } + + n = read(fd, buf, 8192); + if (n < 0) { + fprintf(stderr, "failed to read data\n"); + return 1; + } + + auto req_var = exec("Swoole\\Http\\Request::create"); + + var_dump(req_var); + + if (!req_var.isObject()) { + fprintf(stderr, "cannot create object of Swoole\\Http\\Request\n"); + return 2; + } + + Variant data(buf, n); + + auto req = Object(req_var); + auto retval = req.exec("parse", data); + + printf("retval=%ld", retval.toInt()); + + var_dump(req); + + return 0; +} diff --git a/core-tests/include/httplib_client.h b/core-tests/include/httplib_client.h index 6750258ad4f..aa22c03a87a 100644 --- a/core-tests/include/httplib_client.h +++ b/core-tests/include/httplib_client.h @@ -396,7 +396,7 @@ struct Response { std::function content_provider_resource_releaser_; }; -struct WebSocketFrame : public swWebSocket_frame { +struct WebSocketFrame : public swoole::websocket::Frame { WebSocketFrame() = default; ~WebSocketFrame() { if (payload) { @@ -544,10 +544,10 @@ class Client { std::shared_ptr Put(const char *path, const Headers &headers, const Params ¶ms); // websocket - inline bool Push(const std::string &data, int opcode = WEBSOCKET_OPCODE_TEXT) { - return Push(data.c_str(), data.length(), WEBSOCKET_OPCODE_TEXT); + inline bool Push(const std::string &data, int opcode = swoole::websocket::OPCODE_TEXT) { + return Push(data.c_str(), data.length(), swoole::websocket::OPCODE_TEXT); } - bool Push(const char *data, size_t length, int opcode = WEBSOCKET_OPCODE_TEXT); + bool Push(const char *data, size_t length, int opcode = swoole::websocket::OPCODE_TEXT); std::shared_ptr Recv(); std::shared_ptr Patch(const char *path, const std::string &body, const char *content_type); 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 3e907a7c573..21feb2da8b6 100644 --- a/core-tests/include/test_core.h +++ b/core-tests/include/test_core.h @@ -14,16 +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_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 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 e7a3c331d35..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 coroutine_func_t &_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 coroutine_func_t &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 coroutine_func_t &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: - coroutine_func_t fn; + 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 828da82eda5..00000000000 --- a/core-tests/samples/s1.cc +++ /dev/null @@ -1,60 +0,0 @@ -#include "swoole.h" -#include "swoole_api.h" -#include "client.h" -#include "swoole_server.h" -#include "coroutine.h" -#include "coroutine_socket.h" -#include "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 b279a6dd848..3f6f3fe1395 100644 --- a/core-tests/src/_lib/http.cpp +++ b/core-tests/src/_lib/http.cpp @@ -2,6 +2,8 @@ #include "httplib_client.h" #include "swoole_http.h" +namespace websocket = swoole::websocket; + namespace httplib { bool Client::Upgrade(const char *_path, Headers &_headers) { @@ -29,7 +31,7 @@ bool Client::Push(const char *data, size_t length, int opcode) { buffer.size = sizeof(buf); buffer.str = buf; - swWebSocket_encode(&buffer, data, length, opcode, SW_WEBSOCKET_FLAG_FIN | SW_WEBSOCKET_FLAG_ENCODE_HEADER_ONLY); + websocket::encode(&buffer, data, length, opcode, websocket::FLAG_FIN | websocket::FLAG_ENCODE_HEADER_ONLY); strm.write(buffer.str, buffer.length); strm.write(data, length); return true; @@ -41,7 +43,7 @@ std::shared_ptr Client::Recv() { auto retval = process_socket(socket_, [&](Stream &strm) { swProtocol proto = {}; proto.package_length_size = SW_WEBSOCKET_HEADER_LEN; - proto.get_package_length = swWebSocket_get_package_length; + proto.get_package_length = websocket::get_package_length; proto.package_max_length = SW_INPUT_BUFFER_SIZE; char buf[1024]; @@ -50,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; } @@ -70,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; @@ -83,7 +90,7 @@ std::shared_ptr Client::Recv() { read_bytes += n_read; } - return swWebSocket_decode(msg.get(), data, packet_len); + return websocket::decode(msg.get(), data, packet_len); }); return retval ? msg : nullptr; 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 b5226f4e455..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=%d\n", 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=%d\n", 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=%ld\n", 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 6c0a26d0ee8..af76a7483cb 100644 --- a/core-tests/src/core/log.cpp +++ b/core-tests/src/core/log.cpp @@ -1,28 +1,37 @@ #include "test_core.h" #include "swoole_file.h" #include +#include using namespace swoole; const char *file = "/tmp/swoole_log_test.log"; TEST(log, level) { - sw_logger()->reset(); - sw_logger()->set_level(SW_LOG_NOTICE); - sw_logger()->open(file); + std::vector processTypes = {SW_PROCESS_MASTER, SW_PROCESS_MANAGER, SW_PROCESS_WORKER, SW_PROCESS_TASKWORKER}; - sw_logger()->put(SW_LOG_INFO, SW_STRL("hello info")); - sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello notice")); - sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello warning")); + int originType = swoole_get_process_type(); + for (auto iter = processTypes.begin(); iter != processTypes.end(); iter++) { + SwooleG.process_type = *iter; + sw_logger()->reset(); + sw_logger()->set_level(SW_LOG_NOTICE); + sw_logger()->open(file); - auto content = file_get_contents(file); + sw_logger()->put(SW_LOG_INFO, SW_STRL("hello info")); + sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello notice")); + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello warning")); - sw_logger()->close(); - unlink(file); + auto content = file_get_contents(file); + + sw_logger()->close(); + unlink(file); - ASSERT_FALSE(content->contains(SW_STRL("hello info"))); - ASSERT_TRUE(content->contains(SW_STRL("hello notice"))); - ASSERT_TRUE(content->contains(SW_STRL("hello warning"))); + ASSERT_FALSE(content->contains(SW_STRL("hello info"))); + ASSERT_TRUE(content->contains(SW_STRL("hello notice"))); + ASSERT_TRUE(content->contains(SW_STRL("hello warning"))); + + SwooleG.process_type = originType; + } } TEST(log, date_format) { @@ -85,29 +94,40 @@ TEST(log, date_with_microseconds) { } TEST(log, rotation) { - sw_logger()->reset(); - sw_logger()->set_rotation(SW_LOG_ROTATION_DAILY); - sw_logger()->open(file); - - sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); - - ASSERT_EQ(access(sw_logger()->get_file(), R_OK), -1); - ASSERT_EQ(errno, ENOENT); - ASSERT_EQ(access(sw_logger()->get_real_file(), R_OK), 0); - - sw_logger()->close(); - unlink(sw_logger()->get_real_file()); + std::vector types = { + SW_LOG_ROTATION_DAILY, SW_LOG_ROTATION_EVERY_MINUTE, SW_LOG_ROTATION_HOURLY, SW_LOG_ROTATION_MONTHLY}; + for (auto iter = types.begin(); iter != types.end(); iter++) { + sw_logger()->reset(); + sw_logger()->set_rotation(*iter); + sw_logger()->open(file); + + sw_logger()->put(SW_LOG_DEBUG, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_TRACE, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_NOTICE, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_WARNING, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_ERROR, SW_STRL("hello world")); + sw_logger()->put(SW_LOG_INFO, SW_STRL("hello world")); + + ASSERT_EQ(access(sw_logger()->get_file(), R_OK), -1); + ASSERT_EQ(errno, ENOENT); + ASSERT_EQ(access(sw_logger()->get_real_file(), R_OK), 0); + + sw_logger()->close(); + unlink(sw_logger()->get_real_file()); + } } TEST(log, redirect) { - char *p = getenv("GITHUB_ACTIONS"); - if (p) { + if (test::is_github_ci()) { return; } - sw_logger()->reset(); + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(1)); // no log file opened + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(0)); // no redirected + ASSERT_TRUE(sw_logger()->open(file)); ASSERT_TRUE(sw_logger()->redirect_stdout_and_stderr(1)); + ASSERT_FALSE(sw_logger()->redirect_stdout_and_stderr(1)); // has been redirected printf("hello world\n"); auto content = file_get_contents(file); @@ -119,3 +139,77 @@ TEST(log, redirect) { ASSERT_TRUE(content->contains(SW_STRL("hello world\n"))); } + +namespace TestA { +class TestPrettyName { + public: + static void fun(bool strip, const char *expect_str); +}; + +void TestPrettyName::fun(bool strip, const char *expect_str) { + ASSERT_STREQ(Logger::get_pretty_name(__PRETTY_FUNCTION__, strip).c_str(), 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 + +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"); + test_pretty_name(false, "test_pretty_name"); + + TestA::TestPrettyName::fun(true, "TestPrettyName::fun"); + 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 cf240ea8663..4de8af590bf 100644 --- a/core-tests/src/coroutine/async.cpp +++ b/core-tests/src/coroutine/async.cpp @@ -1,15 +1,12 @@ #include "test_coroutine.h" -#ifdef HAVE_SWOOLE_DIR #include "swoole_async.h" -#else -#include "swoole/swoole_async.h" -#endif + #include #include using namespace std; -using swoole::test::coroutine; using swoole::AsyncEvent; +using swoole::test::coroutine; const int magic_code = 0x7009501; @@ -19,11 +16,11 @@ TEST(coroutine_async, usleep) { bool retval = swoole::coroutine::async( [](AsyncEvent *event) { usleep(1000); - event->ret = magic_code; + event->retval = magic_code; }, ev); ASSERT_EQ(retval, true); - ASSERT_EQ(ev.ret, magic_code); + ASSERT_EQ(ev.retval, magic_code); }); } @@ -52,3 +49,13 @@ TEST(coroutine_async, gethostbyname) { } }); } + +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); }); + 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 bf48abdc0fe..442bb477d6f 100644 --- a/core-tests/src/coroutine/base.cpp +++ b/core-tests/src/coroutine/base.cpp @@ -1,6 +1,7 @@ #include "test_coroutine.h" using namespace swoole; +using swoole::coroutine::System; TEST(coroutine_base, create) { long _cid; @@ -23,12 +24,37 @@ TEST(coroutine_base, get_current) { ASSERT_EQ(cid, _cid); } +TEST(coroutine_base, get_init_msec) { + Coroutine::create([](void *arg) { + auto co = Coroutine::get_current(); + long init_msec = co->get_init_msec(); + + ASSERT_GT(init_msec, 0); + }); +} + 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(); }, @@ -36,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); } @@ -109,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()); }); } @@ -150,3 +178,59 @@ TEST(coroutine_base, get_peak_num) { Coroutine::create( [](void *_arg) { Coroutine::create([](void *_arg) { ASSERT_GE(Coroutine::get_peak_num(), 2); }); }); } + +TEST(coroutine_base, get_elapsed) { + long elapsed_time = 0; + Coroutine::create( + [](void *arg) { + auto co = Coroutine::get_current(); + usleep(2000); + *(long *) arg = Coroutine::get_elapsed(co->get_cid()); + }, + &elapsed_time); + ASSERT_GE(elapsed_time, 2); +} + +TEST(coroutine_base, run) { + long cid = coroutine::run([](void *ptr) { + + }); + ASSERT_GE(cid, 1); +} + +TEST(coroutine_base, cancel) { + coroutine::run([](void *arg) { + auto co = Coroutine::get_current_safe(); + Coroutine::create([co](void *) { + System::sleep(0.002); + co->cancel(); + }); + ASSERT_EQ(co->yield_ex(-1), false); + ASSERT_EQ(co->is_canceled(), true); + }); +} + +TEST(coroutine_base, timeout) { + coroutine::run([](void *arg) { + auto co = Coroutine::get_current_safe(); + ASSERT_EQ(co->yield_ex(0.005), false); + 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 26f2f59cf5a..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; @@ -29,6 +32,13 @@ TEST(coroutine_gethostbyname, resolve_cache) { }); } +TEST(coroutine_gethostbyname, impl_async) { + coroutine::run([](void *arg) { + auto result = swoole::coroutine::gethostbyname_impl_with_async(domain_baidu, AF_INET); + ASSERT_EQ(result.empty(), false); + }); +} + TEST(coroutine_gethostbyname, resolve_cache_inet4_and_inet6) { coroutine::run([](void *arg) { System::set_dns_cache_capacity(10); @@ -54,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 1f39e1b8e73..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,7 +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; @@ -214,25 +218,33 @@ TEST(coroutine_hook, flock) { } TEST(coroutine_hook, read_dir) { - coroutine::run([](void *arg) { + auto fp = opendir("/tmp"); + std::string dir1(readdir(fp)->d_name); + std::string dir2(readdir(fp)->d_name); + closedir(fp); + + auto fn = [&]() { auto fp = swoole_coroutine_opendir("/tmp"); ASSERT_NE(fp, nullptr); struct dirent *entry; entry = swoole_coroutine_readdir(fp); ASSERT_NE(entry, nullptr); - ASSERT_STREQ(entry->d_name, "."); + ASSERT_STREQ(entry->d_name, dir1.c_str()); entry = swoole_coroutine_readdir(fp); ASSERT_NE(entry, nullptr); - ASSERT_STREQ(entry->d_name, ".."); + ASSERT_STREQ(entry->d_name, dir2.c_str()); swoole_coroutine_closedir(fp); - }); + }; + + coroutine::run([&](void *arg) { fn(); }); + fn(); } TEST(coroutine_hook, readlink) { - coroutine::run([](void *arg) { + auto fn = []() { char buf1[1024] = {}; char buf2[1024] = {}; @@ -241,6 +253,359 @@ TEST(coroutine_hook, readlink) { getcwd(buf2, sizeof(buf2)); ASSERT_STREQ(buf1, buf2); + }; + + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, stdio_1) { + auto fn = []() { + FILE *fp1 = swoole_coroutine_fopen(test_file, "w+"); + const char *str = "hello world"; + int n = swoole_coroutine_fputs(str, fp1); + ASSERT_TRUE(n); + swoole_coroutine_fclose(fp1); + + FILE *fp2 = swoole_coroutine_fopen(test_file, "r+"); + char buf[1024]; + char *str2 = swoole_coroutine_fgets(buf, sizeof(buf), fp2); + + ASSERT_STREQ(str2, str); + swoole_coroutine_fclose(fp2); + + unlink(test_file); + }; + + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, stdio_2) { + auto fn = []() { + size_t size = 1024; + + FILE *fp1 = swoole_coroutine_fopen(test_file, "w+"); + String str(size); + str.append_random_bytes(size); + size_t n = swoole_coroutine_fwrite(str.str, 1, size, fp1); + ASSERT_EQ(n, size); + swoole_coroutine_fclose(fp1); + + FILE *fp2 = swoole_coroutine_fopen(test_file, "r+"); + char buf[size]; + size_t len = swoole_coroutine_fread(buf, 1, size, fp2); + ASSERT_EQ(len, size); + + len = swoole_coroutine_fread(buf, 1, size, fp2); + ASSERT_EQ(len, 0); + + ASSERT_TRUE(swoole_coroutine_feof(fp2)); + + ASSERT_MEMEQ(buf, str.str, size); + swoole_coroutine_fclose(fp2); + + unlink(test_file); + }; + + coroutine::run([&](void *arg) { fn(); }); + fn(); +} + +TEST(coroutine_hook, sleep) { + coroutine::run([&](void *arg) { + 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()); + }); +} + +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 104704d5a3e..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,32 +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, 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); + sock.socks5_proxy = create_socks5_proxy(); + proxy_test(sock, false); + }); +} + +TEST(coroutine_socket, http_get_with_http_proxy) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); + sock.http_proxy = create_http_proxy(); + proxy_test(sock, false); + }); +} + +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); + }); +} + +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); + }); +} + +#ifdef SW_USE_OPENSSL +TEST(coroutine_socket, ssl) { + coroutine::run([&](void *arg) { + Socket sock(SW_SOCK_TCP); + + 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"; + + 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); + }); + + 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); + }); +} + +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 f5617fcfb8b..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,6 +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 *) { + 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 741dd58ab97..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" @@ -26,8 +26,14 @@ using swLock = swoole::Lock; using swoole::RWLock; +#ifdef HAVE_SPINLOCK 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; @@ -50,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) { @@ -83,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 ac4be30559c..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 | +----------------------------------------------------------------------+ */ @@ -27,18 +27,24 @@ TEST(global_memory, alloc) { pool->free(ptr1); strcpy(ptr1, "hello, world, #1"); - char *ptr2 = (char *) pool->alloc(12); + char *ptr2 = (char *) pool->alloc(17); strcpy(ptr2, "hello, world, #2"); - char *ptr3 = (char *) pool->alloc(198); + pool->free(ptr2); + + char *ptr3 = (char *) pool->alloc(113); strcpy(ptr3, "hello, world, #3"); ASSERT_TRUE(ptr1); ASSERT_TRUE(ptr2); ASSERT_TRUE(ptr3); - delete pool; + 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"); ASSERT_STREQ(ptr3, "hello, world, #3"); + + pool->destroy(); + delete pool; } 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 a2c83c4c0b9..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 | +----------------------------------------------------------------------+ */ @@ -23,6 +23,7 @@ using namespace swoole; #include +#include struct exception_t : public std::exception { int code; @@ -119,6 +120,10 @@ class table_t { return table->count(); } + Table *ptr() { + return table; + } + ~table_t() { if (table) { table->destroy(); @@ -144,3 +149,184 @@ TEST(table, create) { ASSERT_TRUE(table.del("php")); ASSERT_FALSE(table.exists("php")); } + +void start_iterator(Table *_ptr) { + _ptr->rewind(); + auto count = 0; + while (true) { + _ptr->forward(); + auto row = _ptr->current(); + if (row->key_len == 0) { + break; + } + ASSERT_TRUE(_ptr->exists(row->key, row->key_len)); + count++; + } + 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 7e35175fbf9..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); } @@ -205,3 +219,139 @@ TEST(client, shutdown_all) { retval = cli.recv(&cli, buf, sizeof(buf), 0); ASSERT_EQ(retval, 0); } + +#ifdef SW_USE_OPENSSL + +TEST(client, ssl_1) { + bool connected = false; + bool closed = false; + String buf(65536); + + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + Client client(SW_SOCK_TCP, true); + client.enable_ssl_encrypt(); + client.onConnect = [&connected](Client *cli) { + connected = true; + 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); }; + + ASSERT_EQ(client.connect(&client, TEST_DOMAIN_BAIDU, 443, -1, 0), 0); + + swoole_event_wait(); + + ASSERT_TRUE(connected); + ASSERT_TRUE(closed); + ASSERT_TRUE(buf.contains("Baidu")); +} + +static void proxy_async_test(Client &client, bool https) { + swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + + bool connected = false; + bool closed = false; + String buf(65536); + + if (https) { + client.enable_ssl_encrypt(); + } + + client.onConnect = [&connected](Client *cli) { + connected = true; + 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); }; + + 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("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); +} + +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 5e0f72ab35d..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,26 +21,60 @@ #include "swoole_socket.h" +#include "swoole_util.h" + using namespace swoole; +using swoole::coroutine::Socket; +using swoole::coroutine::System; using namespace swoole::test; using namespace std; -TEST(dns, lookup) { +TEST(dns, lookup1) { + test::coroutine::run([](void *arg) { + auto list = swoole::coroutine::dns_lookup("www.baidu.com", AF_INET, 10); + ASSERT_GE(list.size(), 1); + }); +} + +TEST(dns, lookup_ipv6) { + test::coroutine::run([](void *arg) { + auto list = swoole::coroutine::dns_lookup("www.google.com", AF_INET6, 2); + ASSERT_GE(list.size(), 1); + }); +} + +TEST(dns, domain_not_found) { + test::coroutine::run([](void *arg) { + auto list = swoole::coroutine::dns_lookup("www.baidu.com-not-found", AF_INET, 2); + ASSERT_EQ(list.size(), 0); + ASSERT_EQ(swoole_get_last_error(), SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + }); +} + +TEST(dns, bad_family) { test::coroutine::run([](void *arg) { - auto list = swoole::coroutine::dns_lookup("www.baidu.com", 10); + auto list = swoole::coroutine::dns_lookup("www.google.com", 9999, 2); ASSERT_GE(list.size(), 1); }); } +TEST(dns, cancel) { + // swoole_set_trace_flags(SW_TRACE_CARES); + // swoole_set_log_level(SW_LOG_TRACE); + test::coroutine::run([](void *arg) { + auto co = Coroutine::get_current_safe(); + Coroutine::create([co](void *) { + System::sleep(0.002); + co->cancel(); + }); + auto list1 = swoole::coroutine::dns_lookup("www.baidu-not-found-for-cancel.com", AF_INET, 2); + ASSERT_EQ(list1.size(), 0); + ASSERT_EQ(swoole_get_last_error(), SW_ERROR_CO_CANCELED); + }); +} + 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); @@ -51,3 +85,128 @@ TEST(dns, getaddrinfo) { ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip)); } } + +TEST(dns, 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 new file mode 100644 index 00000000000..5f043f7a431 --- /dev/null +++ b/core-tests/src/os/process_pool.cpp @@ -0,0 +1,230 @@ +#include "test_core.h" +#include "swoole_process_pool.h" + +#include + +#ifdef __MACH__ +#define sysv_signal signal +#endif + +#include "swoole_signal.h" + +using namespace swoole; + +static void test_func(ProcessPool &pool) { + EventData data{}; + 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.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; + String *_data = (String *) pool->ptr; + usleep(10000); + EXPECT_MEMEQ(_data->str, task->data, task->len()); + return 0; + }; + 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) { + 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, svr_port, 128), SW_OK); + + test_func_task_protocol(pool); +} + +TEST(process_pool, unix_sock) { + ProcessPool pool{}; + signal(SIGPIPE, SIG_IGN); + ASSERT_EQ(pool.create(1, 0, SW_IPC_UNIXSOCK), SW_OK); + + test_func_task_protocol(pool); +} + +TEST(process_pool, tcp_raw) { + ProcessPool pool{}; + 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, 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("\0"); + + ASSERT_EQ(pool.dispatch_blocking(data.str, data.length), SW_OK); + + pool.running = true; + pool.ptr = &data; + pool.onMessage = [](ProcessPool *pool, RecvData *rdata) -> void { + pool->running = false; + String *_data = (String *) pool->ptr; + EXPECT_MEMEQ(_data->str, rdata->data, rdata->info.len); + }; + pool.main_loop(&pool, pool.get_worker(0)); + pool.destroy(); +} + +TEST(process_pool, msgqueue) { + ProcessPool pool{}; + ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK); + + 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) { + ProcessPool pool{}; + int *shm_value = (int *) sw_mem_pool()->alloc(sizeof(int)); + ASSERT_EQ(pool.create(1, 0x9501, SW_IPC_MSGQUEUE), SW_OK); + + // init + pool.set_max_packet_size(8192); + pool.set_protocol(SW_PROTOCOL_TASK); + pool.ptr = shm_value; + pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) { + int *shm_value = (int *) pool->ptr; + *shm_value = magic_number; + usleep(1); + }; + + 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; }); + + // 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); +} + +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 71450a3e0c9..98dfdef8d80 100644 --- a/core-tests/src/os/timer.cpp +++ b/core-tests/src/os/timer.cpp @@ -13,34 +13,31 @@ | @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" - -static int timer1_count = 0; -static int timer2_count = 0; -static int timer_running = false; +#include "swoole_timer.h" using swoole::Timer; using swoole::TimerNode; TEST(timer, sys) { - SwooleG.use_signalfd = 0; - timer_running = true; - timer1_count = timer2_count = 0; + int timer1_count = 0; + int timer2_count = 0; + int timer_running = true; 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) { + [&](Timer *, TimerNode *tnode) { timer2_count++; if (timer2_count == 5) { swoole_timer_del(tnode); @@ -63,5 +60,93 @@ TEST(timer, sys) { swoole_timer_free(); - ASSERT_LE(ms2 - ms1, 505); + ASSERT_LE(ms2 - ms1, 510); + ASSERT_EQ(timer1_count, 1); + ASSERT_EQ(timer2_count, 5); +} + +TEST(timer, async) { + int timer1_count = 0; + 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); + + swoole_timer_tick( + 100, + [&](Timer *, TimerNode *tnode) { + timer2_count++; + if (timer2_count == 5) { + swoole_timer_del(tnode); + } + }, + nullptr); + + swoole_event_wait(); + uint64_t ms2 = swoole::time(); + ASSERT_LE(ms2 - ms1, 510); + 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 a27dc161813..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); @@ -173,3 +239,137 @@ TEST(reactor, select) { reactor.wait_exit = true; reactor_test_func(&reactor); } + +TEST(reactor, add_or_update) { + int ret; + UnixSocket p(true, SOCK_DGRAM); + ASSERT_TRUE(p.ready()); + + ret = swoole_event_init(SW_EVENTLOOP_WAIT_EXIT); + ASSERT_EQ(ret, SW_OK); + ASSERT_NE(SwooleTG.reactor, nullptr); + + ret = swoole_event_add_or_update(p.get_socket(false), SW_EVENT_READ); + ASSERT_EQ(ret, SW_OK); + ASSERT_TRUE(p.get_socket(false)->events & SW_EVENT_READ); + + ret = swoole_event_add_or_update(p.get_socket(false), SW_EVENT_WRITE); + ASSERT_EQ(ret, SW_OK); + ASSERT_TRUE(p.get_socket(false)->events & SW_EVENT_READ); + ASSERT_TRUE(p.get_socket(false)->events & SW_EVENT_WRITE); + + 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 84c003e0509..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 == WEBSOCKET_STATUS_ACTIVE) { + if (conn->websocket_status == websocket::STATUS_ACTIVE) { sw_tg_buffer()->clear(); std::string resp = "Swoole: " + string(req->data, req->info.len); - swWebSocket_encode( - sw_tg_buffer(), resp.c_str(), resp.length(), WEBSOCKET_OPCODE_TEXT, SW_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 = 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,33 +601,642 @@ 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(); - websocket_test(serv->get_primary_port()->port, SW_STRL("hello world, swoole is best!")); + 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()->port, str.value(), str.get_length()); + websocket_test(serv->get_primary_port()->get_port(), str.value(), str.get_length()); kill(getpid(), SIGTERM); }); } 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()->port, str.value(), str.get_length()); + 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 3a8c7c76968..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,21 +23,43 @@ #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 beforeReloadPid = 0; + +TEST(server, schedule) { int ret; Server serv(Server::MODE_PROCESS); - serv.create(); + serv.worker_num = 6; + 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].set_status_to_busy(); + } - 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]); + std::set _worker_id_set; + + for (uint32_t i = 0; i < serv.worker_num; i++) { + 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].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); + _worker_id_set.insert(worker_id); + } + ASSERT_EQ(_worker_id_set.size(), serv.worker_num - 2); } static const char *packet = "hello world\n"; @@ -48,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; @@ -57,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]; @@ -71,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); @@ -99,22 +121,347 @@ 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](Server *serv) { + thread t1([=]() { + swoole_signal_block_all(); + + lock->lock(); + + ListenPort *port = serv->get_primary_port(); + + 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(serv->gs->master_pid, SIGTERM); + }); + t1.detach(); + }; + + 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; + }; + + ASSERT_EQ(serv.start(), 0); + + 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); + serv.worker_num = 1; + + SwooleG.running = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + Mutex *lock = new Mutex(Mutex::PROCESS_SHARED); + lock->lock(); + + ListenPort *port = serv.add_port((enum swSocketType)(SW_SOCK_TCP | SW_SOCK_SSL), TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); + exit(2); + } + + 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(); + + ASSERT_EQ(serv.create(), SW_OK); + + serv.onStart = [&lock](Server *serv) { + thread t1([=]() { + swoole_signal_block_all(); + + lock->lock(); + + ListenPort *port = serv->get_primary_port(); + + EXPECT_EQ(port->ssl, 1); + EXPECT_EQ(swoole_ssl_is_thread_safety(), true); + + network::SyncClient c(SW_SOCK_TCP); + 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(); + + kill(serv->gs->master_pid, SIGTERM); + }); + t1.detach(); + }; + + 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; + }; + + ASSERT_EQ(serv.start(), 0); + + delete lock; +} + +TEST(server, dtls) { + Server serv(Server::MODE_BASE); + serv.worker_num = 1; + + SwooleG.running = 1; + + sw_logger()->set_level(SW_LOG_WARNING); + + Mutex *lock = new Mutex(Mutex::PROCESS_SHARED); + lock->lock(); + + ListenPort *port = serv.add_port((enum swSocketType)(SW_SOCK_UDP | SW_SOCK_SSL), TEST_HOST, 0); + if (!port) { + swoole_warning("listen failed, [error=%d]", swoole_get_last_error()); exit(2); } + 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(); + 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); + EXPECT_EQ(port->ssl, 1); + + 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)); @@ -125,9 +472,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); @@ -141,20 +488,69 @@ TEST(server, process) { 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; }; @@ -164,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) { @@ -259,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_empty.php b/examples/coroutine/coro_empty.php index 2a869b09f22..5b57ae8cbf7 100644 --- a/examples/coroutine/coro_empty.php +++ b/examples/coroutine/coro_empty.php @@ -1,8 +1,6 @@ 1]); -var_dump(SWOOLE_CORO_SCHEDULE); co::create(function () { echo "no coro exit\n"; }); 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 16b091983ad..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,36 +72,36 @@ 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]; g_receive_count++; - swConnection *conn = serv->get_connection_by_session_id(req->info.fd); + Connection *conn = serv->get_connection_by_session_id(req->info.fd); 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/curl/hook.php b/examples/curl/hook.php index e78b2fe03d1..0a492b42e9f 100644 --- a/examples/curl/hook.php +++ b/examples/curl/hook.php @@ -1,5 +1,5 @@ SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_CURL | SWOOLE_HOOK_NATIVE_CURL, ]); +Co::set(['hook_flags' => SWOOLE_HOOK_ALL | SWOOLE_HOOK_NATIVE_CURL, ]); //Co::set(['hook_flags' => SWOOLE_HOOK_ALL, ]); Co\run(function () { diff --git a/examples/curl/multi.php b/examples/curl/multi.php new file mode 100644 index 00000000000..b53dda2f2f0 --- /dev/null +++ b/examples/curl/multi.php @@ -0,0 +1,62 @@ +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 a4a6325f742..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, @@ -17,7 +19,10 @@ function dump($var) //'open_cpu_affinity' => 1, //'task_worker_num' => 100, //'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, @@ -36,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); @@ -45,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 @@ -89,6 +94,9 @@ function no_chunk(swoole_http_request $request, swoole_http_response $response) $response->status(404); $response->end(); return; + } else if ($request->server['request_uri'] == '/big_response') { + var_dump($response->end(str_repeat('A', 16 * 1024 * 1024))); + return; } else if ($request->server['request_uri'] == '/code') { $response->sendfile(__FILE__); return; @@ -113,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); @@ -127,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; // }); @@ -150,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); @@ -168,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.

"); } } @@ -178,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"; }); @@ -203,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 44b29a48bbc..781ae316a97 100644 --- a/examples/http2/server.php +++ b/examples/http2/server.php @@ -1,10 +1,10 @@ SWOOLE_TRACE_HTTP2, '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,15 @@ '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', + "e \n ", + null, + 5678, + 3.1415926, + ]); $response->end("

Hello Swoole.

"); }); 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/process_pool/detach.php b/examples/process_pool/detach.php new file mode 100644 index 00000000000..7a613698fa2 --- /dev/null +++ b/examples/process_pool/detach.php @@ -0,0 +1,30 @@ +on('WorkerStart', function (Process\Pool $pool, $workerId) { + echo("[Worker #{$workerId}] WorkerStart\n"); + if ($workerId == 1) { + + } +}); + +$pool->on('WorkerStop', function (\Swoole\Process\Pool $pool, $workerId) { + echo("[Worker #{$workerId}] WorkerStop\n"); +}); + +$pool->on('Message', function ($pool, $msg) { + var_dump($msg); + $pool->detach(); + + while(1) { + sleep(1); + echo "pid=".posix_getpid()."\n"; + }; +}); + +$pool->listen('127.0.0.1', 8089); + +$pool->start(); diff --git a/examples/process_pool/send.php b/examples/process_pool/send.php new file mode 100644 index 00000000000..b9ae046e911 --- /dev/null +++ b/examples/process_pool/send.php @@ -0,0 +1,5 @@ + 'hello', 'uid' => 1991]); +fwrite($fp, pack('N', strlen($msg)) . $msg); +sleep(1); 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 81ef81b4642..cdb1fa25729 100644 --- a/examples/server/echo.php +++ b/examples/server/echo.php @@ -1,24 +1,44 @@ on('connect', function ($serv, $fd, $reactor_id){ -// echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n"; -//}); -$serv->set(array( - 'worker_num' => 1, - -)); - -$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 = new Swoole\Server("0.0.0.0", 9501, SWOOLE_BASE); +// $serv = new Swoole\Server("0.0.0.0", 9501); +$serv = new Swoole\Server("0.0.0.0", 9501, SWOOLE_THREAD); + +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('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 c77054cb7d6..2398b2b9877 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -10,29 +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() -#include "ext/standard/php_var.h" -#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_client.h" #include "swoole_util.h" +#include "swoole_http2.h" #include #include @@ -40,7 +41,7 @@ END_EXTERN_C() #include #include -#if __MACH__ +#if defined(__MACH__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) #include #endif @@ -51,8 +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) @@ -67,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); @@ -77,169 +91,79 @@ 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_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(1, domain) - ZEND_ARG_INFO(1, type) - ZEND_ARG_INFO(1, protocol) -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) +const zend_function_entry swoole_functions[] = { + 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[] */ }; -// clang-format on - -#if PHP_MEMORY_DEBUG -php_vmstat_t php_vmstat; -#endif - -zend_class_entry *swoole_exception_ce; -zend_object_handlers swoole_exception_handlers; - -zend_class_entry *swoole_error_ce; -zend_object_handlers swoole_error_handlers; static const zend_module_dep swoole_deps[] = { -#ifdef SW_USE_JSON - ZEND_MOD_REQUIRED("json") -#endif + ZEND_MOD_REQUIRED("json") #ifdef SW_USE_MYSQLND - ZEND_MOD_REQUIRED("mysqlnd") + ZEND_MOD_REQUIRED("mysqlnd") #endif #ifdef SW_SOCKETS - ZEND_MOD_REQUIRED("sockets") + ZEND_MOD_REQUIRED("sockets") #endif #ifdef SW_USE_CURL - ZEND_MOD_REQUIRED("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 + + ZEND_MOD_END }; -// clang-format off -zend_module_entry swoole_module_entry = -{ +zend_module_entry swoole_module_entry = { STANDARD_MODULE_HEADER_EX, nullptr, swoole_deps, @@ -255,7 +179,16 @@ zend_module_entry swoole_module_entry = }; // clang-format on +zend_class_entry *swoole_exception_ce; +zend_object_handlers swoole_exception_handlers; + +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 @@ -267,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 */ @@ -289,29 +222,35 @@ 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) { php_shutdown_function_entry shutdown_function_entry; +#if PHP_VERSION_ID >= 80100 + zval function_name; + ZVAL_STRING(&function_name, 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); +#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); + register_user_shutdown_function(Z_STRVAL_P(function_name), Z_STRLEN_P(function_name), &shutdown_function_entry); +#endif } void php_swoole_set_global_option(HashTable *vht) { @@ -319,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()); @@ -343,13 +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)) { - if (SwooleG.dns_server_v4) { - sw_free(SwooleG.dns_server_v4); - } - SwooleG.dns_server_v4 = zend::String(ztmp).dup(); + 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) { @@ -358,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); } @@ -374,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) { @@ -389,18 +349,80 @@ SW_API bool php_swoole_is_enable_coroutine() { if (sw_server()) { return sw_server()->is_enable_coroutine(); } else { - return SWOOLE_G(enable_coroutine); + return SwooleG.enable_coroutine; + } +} + +SW_API zend_long php_swoole_parse_to_size(zval *zv) { + if (ZVAL_IS_STRING(zv)) { +#if PHP_VERSION_ID >= 80200 + zend_string *errstr; + auto size = zend_ini_parse_quantity(Z_STR_P(zv), &errstr); + if (errstr) { + php_swoole_fatal_error( + E_ERROR, "failed to parse '%s' to size, Error: %s", Z_STRVAL_P(zv), ZSTR_VAL(errstr)); + zend_string_release(errstr); + } + return size; +#else + return zend_atol(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); +#endif + } else { + return zval_get_long(zv); } } +SW_API zend_string *php_swoole_serialize(zval *zdata) { + php_serialize_data_t var_hash; + smart_str serialized_data = {0}; + + PHP_VAR_SERIALIZE_INIT(var_hash); + php_var_serialize(&serialized_data, zdata, &var_hash); + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + zend_string *result = nullptr; + if (!EG(exception)) { + result = zend_string_init(serialized_data.s->val, serialized_data.s->len, 1); + } + smart_str_free(&serialized_data); + return result; +} + +SW_API bool php_swoole_unserialize(zend_string *data, zval *zv) { + php_unserialize_data_t var_hash; + const char *p = ZSTR_VAL(data); + size_t l = ZSTR_LEN(data); + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + zend_bool unserialized = php_var_unserialize(zv, (const uchar **) &p, (const uchar *) (p + l), &var_hash); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if (!unserialized) { + swoole_warning("unserialize() failed, Error at offset " ZEND_LONG_FMT " of %zd bytes", + (zend_long) ((char *) p - ZSTR_VAL(data)), + l); + } + return unserialized; +} + static void fatal_error(int code, const char *format, ...) { va_list args; - zend_object *exception; va_start(args, format); - exception = zend_throw_exception(swoole_error_ce, swoole::std_string::vformat(format, args).c_str(), code); + zend_object *exception = + zend_throw_exception(swoole_error_ce, swoole::std_string::vformat(format, args).c_str(), code); va_end(args); - zend_exception_error(exception, E_ERROR); - exit(255); + + zend::print_error(exception, E_ERROR); + +#ifdef SW_THREAD + if (!tsrm_is_main_thread()) { + php_swoole_thread_bailout(); + } +#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 @@ -432,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 */ @@ -570,14 +576,22 @@ 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); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_DNSLOOKUP_DUPLICATE_REQUEST", SW_ERROR_DNSLOOKUP_DUPLICATE_REQUEST); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED", SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT", SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_DNSLOOKUP_UNSUPPORTED", SW_ERROR_DNSLOOKUP_UNSUPPORTED); + 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); @@ -600,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); @@ -607,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); @@ -622,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); @@ -637,7 +659,14 @@ 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); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_OUT_OF_COROUTINE", SW_ERROR_CO_OUT_OF_COROUTINE); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_HAS_BEEN_BOUND", SW_ERROR_CO_HAS_BEEN_BOUND); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_HAS_BEEN_DISCARDED", SW_ERROR_CO_HAS_BEEN_DISCARDED); @@ -652,6 +681,11 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_PROTECT_STACK_FAILED", SW_ERROR_CO_PROTECT_STACK_FAILED); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_STD_THREAD_LINK_ERROR", SW_ERROR_CO_STD_THREAD_LINK_ERROR); SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_DISABLED_MULTI_THREAD", SW_ERROR_CO_DISABLED_MULTI_THREAD); + SW_REGISTER_LONG_CONSTANT("SWOOLE_ERROR_CO_CANNOT_CANCEL", SW_ERROR_CO_CANNOT_CANCEL); + 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 @@ -670,7 +704,7 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_EOF_PROTOCOL", SW_TRACE_EOF_PROTOCOL); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_LENGTH_PROTOCOL", SW_TRACE_LENGTH_PROTOCOL); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CLOSE", SW_TRACE_CLOSE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_WEBSOCEKT", SW_TRACE_WEBSOCEKT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_WEBSOCKET", SW_TRACE_WEBSOCKET); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_REDIS_CLIENT", SW_TRACE_REDIS_CLIENT); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_MYSQL_CLIENT", SW_TRACE_MYSQL_CLIENT); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_HTTP_CLIENT", SW_TRACE_HTTP_CLIENT); @@ -684,6 +718,13 @@ PHP_MINIT_FUNCTION(swoole) { SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CONTEXT", SW_TRACE_CONTEXT); SW_REGISTER_LONG_CONSTANT("SWOOLE_TRACE_CO_HTTP_SERVER", SW_TRACE_CO_HTTP_SERVER); 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); /** @@ -712,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_error, "Swoole\\Error", "swoole_error", nullptr, nullptr, zend_ce_error, zend_get_std_object_handlers()); + swoole_exception, "Swoole\\Exception", nullptr, nullptr, zend_ce_exception, zend_get_std_object_handlers()); + + SW_INIT_CLASS_ENTRY_EX2( + swoole_error, "Swoole\\Error", nullptr, nullptr, zend_ce_error, zend_get_std_object_handlers()); /** **/ php_swoole_event_minit(module_number); @@ -748,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 @@ -789,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; } @@ -807,16 +887,19 @@ PHP_MINFO_FUNCTION(swoole) { php_info_print_table_row(2, "Version", SWOOLE_VERSION); snprintf(buf, sizeof(buf), "%s %s", __DATE__, __TIME__); php_info_print_table_row(2, "Built", buf); - php_info_print_table_row(2, "coroutine", "enabled"); +#if defined(SW_USE_THREAD_CONTEXT) + php_info_print_table_row(2, "coroutine", "enabled with thread context"); +#elif defined(SW_USE_ASM_CONTEXT) + php_info_print_table_row(2, "coroutine", "enabled with boost asm context"); +#else + php_info_print_table_row(2, "coroutine", "enabled with ucontext"); +#endif #ifdef SW_DEBUG php_info_print_table_row(2, "debug", "enabled"); #endif #ifdef SW_LOG_TRACE_OPEN php_info_print_table_row(2, "trace_log", "enabled"); #endif -#ifdef SW_NO_USE_ASM_CONTEXT - php_info_print_table_row(2, "ucontext", "enabled"); -#endif #ifdef HAVE_EPOLL php_info_print_table_row(2, "epoll", "enabled"); #endif @@ -826,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 @@ -850,18 +936,17 @@ PHP_MINFO_FUNCTION(swoole) { #else php_info_print_table_row(2, "openssl", "enabled"); #endif +#ifdef SW_SUPPORT_DTLS + php_info_print_table_row(2, "dtls", "enabled"); #endif -#ifdef SW_USE_HTTP2 - php_info_print_table_row(2, "http2", "enabled"); #endif -#ifdef SW_USE_JSON + php_info_print_table_row(2, "http2", "enabled"); 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"); +#ifdef SW_USE_CARES + php_info_print_table_row(2, "c-ares", ares_version(nullptr)); #endif #ifdef SW_HAVE_ZLIB #ifdef ZLIB_VERSION @@ -874,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 @@ -892,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(); @@ -935,7 +1038,7 @@ static void *_sw_zend_string_calloc(size_t nmemb, size_t size) { } static void *_sw_zend_string_realloc(void *address, size_t size) { - zend_string *str = zend_string_realloc(sw_get_zend_string(address), size, 0); + zend_string *str = zend_string_realloc(zend::fetch_zend_string_by_val(address), size, 0); if (str == nullptr) { return nullptr; } @@ -943,17 +1046,17 @@ static void *_sw_zend_string_realloc(void *address, size_t size) { } static void _sw_zend_string_free(void *address) { - zend_string_free((zend_string *) (sw_get_zend_string(address))); + zend_string_free(zend::fetch_zend_string_by_val(address)); } -static swoole::Allocator php_allocator { +static swoole::Allocator php_allocator{ _sw_emalloc, _sw_ecalloc, _sw_erealloc, _sw_efree, }; -static swoole::Allocator zend_string_allocator { +static swoole::Allocator zend_string_allocator{ _sw_zend_string_malloc, _sw_zend_string_calloc, _sw_zend_string_realloc, @@ -968,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"); @@ -984,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 @@ -992,6 +1154,19 @@ 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; return SUCCESS; @@ -1006,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 @@ -1028,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; } @@ -1076,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; } } @@ -1120,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) @@ -1130,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; @@ -1311,44 +1514,46 @@ static PHP_FUNCTION(swoole_internal_call_user_shutdown_begin) { static PHP_FUNCTION(swoole_substr_unserialize) { zend_long offset, length = 0; - char *buf = NULL; - size_t buf_len; + char *buf = NULL; + size_t buf_len; zval *options = NULL; - ZEND_PARSE_PARAMETERS_START(2, 4) + ZEND_PARSE_PARAMETERS_START(2, 4) Z_PARAM_STRING(buf, buf_len) Z_PARAM_LONG(offset) Z_PARAM_OPTIONAL Z_PARAM_LONG(length) Z_PARAM_ARRAY(options) - ZEND_PARSE_PARAMETERS_END(); + ZEND_PARSE_PARAMETERS_END(); if (buf_len == 0) { RETURN_FALSE; } 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; - size_t str_len; - zend_bool assoc = 0; /* return JS objects as PHP objects by default */ - zend_bool assoc_null = 1; - zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH; - zend_long options = 0; + size_t str_len; + zend_bool assoc = 0; /* return JS objects as PHP objects by default */ + zend_bool assoc_null = 1; + zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH; + zend_long options = 0; - ZEND_PARSE_PARAMETERS_START(2, 6) + ZEND_PARSE_PARAMETERS_START(2, 6) Z_PARAM_STRING(str, str_len) Z_PARAM_LONG(offset) Z_PARAM_OPTIONAL @@ -1356,29 +1561,70 @@ static PHP_FUNCTION(swoole_substr_json_decode) { Z_PARAM_BOOL_EX(assoc, assoc_null, 1, 0) Z_PARAM_LONG(depth) Z_PARAM_LONG(options) - ZEND_PARSE_PARAMETERS_END(); + 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 */ if (!assoc_null) { if (assoc) { - options |= PHP_JSON_OBJECT_AS_ARRAY; + options |= PHP_JSON_OBJECT_AS_ARRAY; } else { options &= ~PHP_JSON_OBJECT_AS_ARRAY; } } 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.h b/ext-src/php_swoole.h deleted file mode 100644 index 596ad1a12e3..00000000000 --- a/ext-src/php_swoole.h +++ /dev/null @@ -1,1146 +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 | - +----------------------------------------------------------------------+ -*/ - -#ifndef PHP_SWOOLE_H -#define PHP_SWOOLE_H - -// C++ build format macros must defined earlier -#ifdef __cplusplus -#define __STDC_FORMAT_MACROS -#endif - -#include "php.h" -#include "php_ini.h" -#include "php_globals.h" -#include "php_main.h" - -#include "php_streams.h" -#include "php_network.h" - -#include "zend_variables.h" -#include "zend_interfaces.h" -#include "zend_closures.h" -#include "zend_exceptions.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define SW_HAVE_COUNTABLE 1 - -#include "swoole_api.h" -#include "swoole_async.h" - -#ifdef SW_HAVE_ZLIB -#include -#endif - -BEGIN_EXTERN_C() -#include -#include -#include -#include -#include -#include -#include - -#define PHP_SWOOLE_VERSION SWOOLE_VERSION -#define PHP_SWOOLE_CLIENT_USE_POLL - -extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); - -extern zend_module_entry swoole_module_entry; - -#ifdef PHP_WIN32 -# define PHP_SWOOLE_API __declspec(dllexport) -#elif defined(__GNUC__) && __GNUC__ >= 4 -# define PHP_SWOOLE_API __attribute__ ((visibility("default"))) -#else -# define PHP_SWOOLE_API -#endif - -#define SW_CHECK_RETURN(s) if(s<0){RETURN_FALSE;}else{RETURN_TRUE;} -#define SW_LOCK_CHECK_RETURN(s) if(s==0){RETURN_TRUE;}else{zend_update_property_long(NULL,SW_Z8_OBJ_P(ZEND_THIS),SW_STRL("errCode"),s);RETURN_FALSE;} - -#define php_swoole_fatal_error(level, fmt_str, ...) \ - php_error_docref(NULL, level, (const char *) (fmt_str), ##__VA_ARGS__) - -#define php_swoole_error(level, fmt_str, ...) \ - if (SWOOLE_G(display_errors) || level == E_ERROR) \ - php_swoole_fatal_error(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) - -#ifdef SW_USE_OPENSSL -#ifndef HAVE_OPENSSL -#error "Enable openssl support, require openssl library" -#endif -#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" -#endif - -//-------------------------------------------------------- -#define SW_MAX_FIND_COUNT 100 //for swoole_server::connection_list -#define SW_PHP_CLIENT_BUFFER_SIZE 65535 -//-------------------------------------------------------- -enum php_swoole_client_callback_type -{ - SW_CLIENT_CB_onConnect = 1, - SW_CLIENT_CB_onReceive, - SW_CLIENT_CB_onClose, - SW_CLIENT_CB_onError, - SW_CLIENT_CB_onBufferFull, - SW_CLIENT_CB_onBufferEmpty, -#ifdef SW_USE_OPENSSL - SW_CLIENT_CB_onSSLReady, -#endif -}; -//--------------------------------------------------------- -#define SW_FLAG_KEEP (1u << 12) -#define SW_FLAG_ASYNC (1u << 10) -#define SW_FLAG_SYNC (1u << 11) -//--------------------------------------------------------- -enum php_swoole_fd_type -{ - PHP_SWOOLE_FD_STREAM_CLIENT = SW_FD_STREAM_CLIENT, - PHP_SWOOLE_FD_DGRAM_CLIENT, - PHP_SWOOLE_FD_MYSQL, - PHP_SWOOLE_FD_REDIS, - PHP_SWOOLE_FD_HTTPCLIENT, - PHP_SWOOLE_FD_PROCESS_STREAM, - PHP_SWOOLE_FD_MYSQL_CORO, - 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, -}; -//--------------------------------------------------------- -typedef enum -{ - PHP_SWOOLE_RINIT_BEGIN, - PHP_SWOOLE_RINIT_END, - PHP_SWOOLE_CALL_USER_SHUTDOWNFUNC_BEGIN, - PHP_SWOOLE_RSHUTDOWN_BEGIN, - PHP_SWOOLE_RSHUTDOWN_END, -} php_swoole_req_status; -//--------------------------------------------------------- - -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)); -} - -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_class_entry *swoole_server_ce; -extern zend_object_handlers swoole_server_handlers; -extern zend_class_entry *swoole_connection_iterator_ce; -extern zend_class_entry *swoole_process_ce; -extern zend_class_entry *swoole_http_server_ce; -extern zend_object_handlers swoole_http_server_handlers; -extern zend_class_entry *swoole_websocket_server_ce; -extern zend_class_entry *swoole_websocket_frame_ce; -extern zend_class_entry *swoole_server_port_ce; -extern zend_class_entry *swoole_exception_ce; -extern zend_object_handlers swoole_exception_handlers; - -PHP_MINIT_FUNCTION(swoole); -PHP_MSHUTDOWN_FUNCTION(swoole); -PHP_RINIT_FUNCTION(swoole); -PHP_RSHUTDOWN_FUNCTION(swoole); -PHP_MINFO_FUNCTION(swoole); - -PHP_FUNCTION(swoole_clear_dns_cache); -PHP_FUNCTION(swoole_last_error); -PHP_FUNCTION(swoole_set_process_name); -//--------------------------------------------------------- -// Coroutine API -//--------------------------------------------------------- -PHP_FUNCTION(swoole_coroutine_create); -PHP_FUNCTION(swoole_coroutine_exec); -PHP_FUNCTION(swoole_coroutine_gethostbyname); -PHP_FUNCTION(swoole_coroutine_defer); -PHP_FUNCTION(swoole_coroutine_socketpair); -//--------------------------------------------------------- -// event -//--------------------------------------------------------- -PHP_FUNCTION(swoole_client_select); -//--------------------------------------------------------- -// async[coro] -//--------------------------------------------------------- -PHP_FUNCTION(swoole_async_set); -PHP_FUNCTION(swoole_async_dns_lookup_coro); -//--------------------------------------------------------- -// error -//--------------------------------------------------------- -#define SW_STRERROR_SYSTEM 0 -#define SW_STRERROR_GAI 1 -#define SW_STRERROR_DNS 2 -#define SW_STRERROR_SWOOLE 9 - -/** - * MINIT - * ============================================================== - */ -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); -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); -void php_swoole_timer_minit(int module_number); -// coroutine -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_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_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); -#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_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); - -/** - * RSHUTDOWN - * ============================================================== - */ -void php_swoole_async_coro_rshutdown(); -void php_swoole_redis_server_rshutdown(); -void php_swoole_coroutine_rshutdown(); -void php_swoole_runtime_rshutdown(); -void php_swoole_server_rshutdown(); - -int php_swoole_reactor_init(); -void php_swoole_set_global_option(zend_array *vht); -void php_swoole_set_coroutine_option(zend_array *vht); -void php_swoole_set_aio_option(zend_array *vht); - -// shutdown -void php_swoole_register_shutdown_function(const char *function); -void php_swoole_register_shutdown_function_prepend(const char *function); - -// event -void php_swoole_event_init(); -void php_swoole_event_wait(); -void php_swoole_event_exit(); - -/** - * MSHUTDOWN - * ============================================================== - */ -void php_swoole_runtime_mshutdown() ; - -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); -} - -static sw_inline size_t php_swoole_get_send_data(zval *zdata, char **str) -{ - convert_to_string(zdata); - *str = Z_STRVAL_P(zdata); - return Z_STRLEN_P(zdata); -} - -int php_swoole_convert_to_fd(zval *zsocket); -int php_swoole_convert_to_fd_ex(zval *zsocket, int *async); - -#ifdef SWOOLE_SOCKETS_SUPPORT -php_socket *php_swoole_convert_to_socket(int sock); -#endif - -zend_bool php_swoole_signal_isset_handler(int signo); - -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; - long socket_buffer_size; - php_swoole_req_status req_status; -ZEND_END_MODULE_GLOBALS(swoole) - -extern ZEND_DECLARE_MODULE_GLOBALS(swoole); - -#ifdef ZTS -#define SWOOLE_G(v) TSRMG(swoole_globals_id, zend_swoole_globals *, v) -#else -#define SWOOLE_G(v) (swoole_globals.v) -#endif - -/* PHP 7 compatibility patches */ -#define sw_zend_bailout() zend_bailout() - -// Fixed in php-7.2.3RC1 (https://github.com/php/php-src/commit/e88e83d3e5c33fcd76f08b23e1a2e4e8dc98ce41) -#if PHP_MAJOR_VERSION == 7 && ((PHP_MINOR_VERSION == 2 && PHP_RELEASE_VERSION < 3)) -// See https://github.com/php/php-src/commit/0495bf5650995cd8f18d6a9909eb4c5dcefde669 -// Then https://github.com/php/php-src/commit/2dcfd8d16f5fa69582015cbd882aff833075a34c -// See https://github.com/php/php-src/commit/52db03b3e52bfc886896925d050af79bc4dc1ba3 -#if PHP_MINOR_VERSION == 2 -#define SW_ZEND_WRONG_PARAMETERS_COUNT_ERROR zend_wrong_parameters_count_error(_flags & ZEND_PARSE_PARAMS_THROW, _num_args, _min_num_args, _max_num_args) -#else -#define SW_ZEND_WRONG_PARAMETERS_COUNT_ERROR zend_wrong_parameters_count_error(_num_args, _min_num_args, _max_num_args) -#endif - -#undef ZEND_PARSE_PARAMETERS_START_EX - -#define ZEND_PARSE_PARAMETERS_START_EX(flags, min_num_args, max_num_args) do { \ - const int _flags = (flags); \ - int _min_num_args = (min_num_args); \ - int _max_num_args = (max_num_args); \ - int _num_args = EX_NUM_ARGS(); \ - int _i; \ - zval *_real_arg, *_arg = NULL; \ - zend_expected_type _expected_type = Z_EXPECTED_LONG; \ - char *_error = NULL; \ - zend_bool _dummy; \ - zend_bool _optional = 0; \ - int error_code = ZPP_ERROR_OK; \ - ((void)_i); \ - ((void)_real_arg); \ - ((void)_arg); \ - ((void)_expected_type); \ - ((void)_error); \ - ((void)_dummy); \ - ((void)_optional); \ - \ - do { \ - if (UNEXPECTED(_num_args < _min_num_args) || \ - (UNEXPECTED(_num_args > _max_num_args) && \ - EXPECTED(_max_num_args >= 0))) { \ - if (!(_flags & ZEND_PARSE_PARAMS_QUIET)) { \ - SW_ZEND_WRONG_PARAMETERS_COUNT_ERROR; \ - } \ - error_code = ZPP_ERROR_FAILURE; \ - break; \ - } \ - _i = 0; \ - _real_arg = ZEND_CALL_ARG(execute_data, 0); -#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 - -#ifndef E_FATAL_ERRORS -#define E_FATAL_ERRORS (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR | E_PARSE) -#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 -/*}}}*/ - -/* PHP 7 wrapper functions / macros */ - -//----------------------------------Zval API------------------------------------ - -// Deprecated: do not use it anymore -// do not use sw_copy_to_stack(return_value, foo); -#define sw_copy_to_stack(ptr, val) do { \ - (val) = *(zval *) (ptr); \ - (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) -{ - return Z_TYPE_P(v) == IS_TRUE || Z_TYPE_P(v) == IS_FALSE; -} -#endif - -#ifndef ZVAL_IS_TRUE -static sw_inline zend_bool ZVAL_IS_TRUE(zval *v) -{ - return Z_TYPE_P(v) == IS_TRUE; -} -#endif - -#ifndef ZVAL_IS_FALSE -static sw_inline zend_bool ZVAL_IS_FALSE(zval *v) -{ - return Z_TYPE_P(v) == IS_FALSE; -} -#endif - -#ifndef ZVAL_IS_LONG -static sw_inline zend_bool ZVAL_IS_LONG(zval *v) -{ - return Z_TYPE_P(v) == IS_LONG; -} -#endif - -#ifndef ZVAL_IS_STRING -static sw_inline zend_bool ZVAL_IS_STRING(zval *v) -{ - return Z_TYPE_P(v) == IS_STRING; -} -#endif - -#ifndef Z_BVAL_P -static sw_inline zend_bool Z_BVAL_P(zval *v) -{ - return Z_TYPE_P(v) == IS_TRUE; -} -#endif - -#ifndef ZVAL_IS_ARRAY -static sw_inline zend_bool ZVAL_IS_ARRAY(zval *v) -{ - return Z_TYPE_P(v) == IS_ARRAY; -} -#endif - -static sw_inline zval *sw_malloc_zval() -{ - return (zval *) emalloc(sizeof(zval)); -} - -static sw_inline zend_string *sw_get_zend_string(void *addr) -{ - return (zend_string *) ((char *) addr - offsetof(zend_string, val)); -} - -static sw_inline void sw_set_zend_string(zval *zdata, char *addr, size_t length) -{ - zend_string *zstr = sw_get_zend_string(addr); - addr[length] = 0; - zstr->len = length; - ZVAL_STR(zdata, zstr); -} - -static sw_inline zval *sw_zval_dup(zval *val) -{ - zval *dup = sw_malloc_zval(); - memcpy(dup, val, sizeof(zval)); - return dup; -} - -static sw_inline void sw_zval_free(zval *val) -{ - zval_ptr_dtor(val); - efree(val); -} - -//----------------------------------Constant API------------------------------------ - -#define SW_REGISTER_NULL_CONSTANT(name) REGISTER_NULL_CONSTANT(name, CONST_CS | CONST_PERSISTENT) -#define SW_REGISTER_BOOL_CONSTANT(name, value) REGISTER_BOOL_CONSTANT(name, value, CONST_CS | CONST_PERSISTENT) -#define SW_REGISTER_LONG_CONSTANT(name, value) REGISTER_LONG_CONSTANT(name, value, CONST_CS | CONST_PERSISTENT) -#define SW_REGISTER_DOUBLE_CONSTANT(name, value) REGISTER_DOUBLE_CONSTANT(name, value, CONST_CS | CONST_PERSISTENT) -#define SW_REGISTER_STRING_CONSTANT(name, value) REGISTER_STRING_CONSTANT(name, (char *) value, CONST_CS | CONST_PERSISTENT) -#define SW_REGISTER_STRINGL_CONSTANT(name, value) REGISTER_STRINGL_CONSTANT(name, (char *) value, CONST_CS | CONST_PERSISTENT) - -//----------------------------------Number API----------------------------------- - -#define sw_php_math_round(value, places, mode) _php_math_round(value, places, mode) - -//----------------------------------String API----------------------------------- - -#define SW_PHP_OB_START(zoutput) \ - zval zoutput; \ - do { \ - php_output_start_user(NULL, 0, PHP_OUTPUT_HANDLER_STDFLAGS); -#define SW_PHP_OB_END() \ - php_output_get_contents(&zoutput); \ - php_output_discard(); \ - } while (0) - -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) - { - s = zend_string_realloc(s, real_len, 0); - } - else - { - ZSTR_LEN(s) = real_len; - } - } - ZSTR_VAL(s)[real_len] = '\0'; - return s; -} - -//----------------------------------Array API------------------------------------ - -#define php_swoole_array_length(zarray) zend_hash_num_elements(Z_ARRVAL_P(zarray)) -#define php_swoole_array_get_value(ht, str, v) ((v = zend_hash_str_find(ht, str, sizeof(str)-1)) && !ZVAL_IS_NULL(v)) -#define php_swoole_array_get_value_ex(ht, str, v) ((v = zend_hash_str_find(ht, str, strlen(str))) && !ZVAL_IS_NULL(v)) - -static sw_inline int php_swoole_array_length_safe(zval *zarray) -{ - if (zarray && ZVAL_IS_ARRAY(zarray)) - { - return php_swoole_array_length(zarray); - } - else - { - return 0; - } -} - -void php_swoole_sha1(const char *str, int _len, uchar *digest); -void php_swoole_sha256(const char *str, int _len, uchar *digest); - -#define SW_HASHTABLE_FOREACH_START(ht, _val) ZEND_HASH_FOREACH_VAL(ht, _val); { -#define SW_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, _val) zend_string *_foreach_key;\ - ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _foreach_key, _val); \ - if (!_foreach_key) {k = NULL; klen = 0; ktype = 0;} \ - else {k = ZSTR_VAL(_foreach_key), klen=ZSTR_LEN(_foreach_key); ktype = 1;} { -#define SW_HASHTABLE_FOREACH_END() } ZEND_HASH_FOREACH_END(); - -static sw_inline void add_assoc_ulong_safe_ex(zval *arg, const char *key, size_t key_len, zend_ulong value) -{ - if (sw_likely(value <= ZEND_LONG_MAX)) - { - add_assoc_long_ex(arg, key, key_len, value); - } - else - { - char buf[MAX_LENGTH_OF_LONG + 1]; - size_t len = sw_snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, value); - add_assoc_stringl_ex(arg, key, key_len, buf, len); - } -} - -static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulong value) -{ - add_assoc_ulong_safe_ex(arg, key, strlen(key), value); -} - -//----------------------------------Class API------------------------------------ - -#define SW_Z_OBJCE_NAME_VAL_P(zobject) ZSTR_VAL(Z_OBJCE_P(zobject)->name) - -/* PHP 7 class declaration macros */ - -#define SW_INIT_CLASS_ENTRY_BASE(module, namespace_name, snake_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); \ - 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); \ - 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); \ - 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); \ - memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) - -#define SW_CLASS_ALIAS(name, module) do { \ - if (name) { \ - sw_zend_register_class_alias(ZEND_STRL(name), module##_ce); \ - } \ -} while (0) - -#define SW_CLASS_ALIAS_SHORT_NAME(short_name, module) do { \ - if (SWOOLE_G(use_shortname)) { \ - SW_CLASS_ALIAS(short_name, module); \ - } \ -} while (0) - -#define SW_SET_CLASS_SERIALIZABLE(module, _serialize, _unserialize) \ - module##_ce->serialize = _serialize; \ - module##_ce->unserialize = _unserialize - -#define sw_zend_class_clone_deny NULL -#define SW_SET_CLASS_CLONEABLE(module, _clone_obj) \ - module##_handlers.clone_obj = _clone_obj - -#define SW_SET_CLASS_UNSET_PROPERTY_HANDLER(module, _unset_property) \ - module##_handlers.unset_property = _unset_property - -#define SW_SET_CLASS_CREATE(module, _create_object) \ - module##_ce->create_object = _create_object - -#define SW_SET_CLASS_DTOR(module, _dtor_obj) \ - module##_handlers.dtor_obj = _dtor_obj - -#define SW_SET_CLASS_FREE(module, _free_obj) \ - module##_handlers.free_obj = _free_obj - -#define SW_SET_CLASS_CREATE_AND_FREE(module, _create_object, _free_obj) \ - SW_SET_CLASS_CREATE(module, _create_object); \ - SW_SET_CLASS_FREE(module, _free_obj) - -#define SW_SET_CLASS_CUSTOM_OBJECT(module, _create_object, _free_obj, _struct, _std) \ - SW_SET_CLASS_CREATE_AND_FREE(module, _create_object, _free_obj); \ - module##_handlers.offset = XtOffsetOf(_struct, _std) - -#define SW_PREVENT_USER_DESTRUCT() do { \ - if (sw_unlikely(!(GC_FLAGS(Z_OBJ_P(ZEND_THIS)) & IS_OBJ_DESTRUCTOR_CALLED))) { \ - RETURN_NULL(); \ - } \ -} 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)) - -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 -) -{ - 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); - zend_string_release(lowercase_origin); - if (UNEXPECTED(!origin_function)) - { - return FAILURE; - } - 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); - efree(_alias); - return ret; -} - -static sw_inline int sw_zend_register_class_alias(const char *name, size_t name_len, zend_class_entry *ce) -{ - zend_string *_name; - if (name[0] == '\\') - { - _name = zend_string_init(name, name_len, 1); - zend_str_tolower_copy(ZSTR_VAL(_name), name + 1, name_len - 1); - } - else - { - _name = zend_string_init(name, name_len, 1); - zend_str_tolower_copy(ZSTR_VAL(_name), name, name_len); - } - - 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); - object_properties_init(object, ce); - object->handlers = handlers; - return object; -} - -static sw_inline zend_object* sw_zend_create_object_deny(zend_class_entry *ce) -{ - zend_object *object; - object = zend_objects_new(ce); - /* Initialize default properties */ - if (EXPECTED(ce->default_properties_count != 0)) { - zval *p = object->properties_table; - zval *end = p + ce->default_properties_count; - do { - ZVAL_UNDEF(p); - p++; - } while (p != end); - } - zend_throw_error(NULL, "The object of %s can not be created for security reasons", ZSTR_VAL(ce->name)); - 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) - { - ce = ce->parent; - } - SW_ASSERT(ce->type == ZEND_INTERNAL_CLASS); - if (EXPECTED(zend_hash_find(&ce->properties_info, member))) - { - zend_throw_error(NULL, "Property %s of class %s cannot be unset", ZSTR_VAL(member), ZSTR_VAL(object->ce->name)); - return; - } - 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) -{ - 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); - return zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv); - } - return property; -} - -static sw_inline void sw_zend_update_property_null_ex(zend_class_entry *scope, zval *object, zend_string *s) -{ - zval tmp; - - ZVAL_NULL(&tmp); - 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); - 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); - } - 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) -{ - 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; -} - -static sw_inline zval* sw_zend_read_property_not_null_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); - zend_uchar type = Z_TYPE_P(property); - 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) -{ - zval ztmp; - array_init(&ztmp); - zend_update_property(ce, SW_Z8_OBJ_P(obj), s, len, &ztmp); - zval_ptr_dtor(&ztmp); - return zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, 1, &ztmp); -} - -static sw_inline zval* sw_zend_read_and_convert_property_array(zend_class_entry *ce, zval *obj, const char *s, int 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 - if (UNEXPECTED(property == &EG(uninitialized_zval))) - { - property = sw_zend_update_and_read_property_array(ce, obj, s, len); - } - else - { - zval_ptr_dtor(property); - array_init(property); - } - } - - return property; -} - -#define SW_RETURN_PROPERTY(name) do { \ - RETURN_ZVAL(sw_zend_read_property(Z_OBJCE_P(ZEND_THIS), ZEND_THIS, ZEND_STRL(name), 0), 1, 0); \ -} while (0) - -#define RETURN_SW_STRING(buf) do { \ - RETURN_STRINGL(buf->str, buf->length); \ -} while (0) - -//----------------------------------Function API------------------------------------ - -/** - * Notice (sw_zend_call_method_with_%u_params): If you don't want to check the return value, please set retval to NULL - */ -#define sw_zend_call_method_with_0_params(zobj, obj_ce, fn_ptr_ptr, fn_name, retval) \ - zend_call_method_with_0_params(SW_Z8_OBJ_P(zobj), obj_ce, fn_ptr_ptr, fn_name, retval) - -#define sw_zend_call_method_with_1_params(zobj, obj_ce, fn_ptr_ptr, fn_name, retval, v1) \ - zend_call_method_with_1_params(SW_Z8_OBJ_P(zobj), obj_ce, fn_ptr_ptr, fn_name, retval, v1) - -#define sw_zend_call_method_with_2_params(zobj, obj_ce, fn_ptr_ptr, fn_name, retval, v1, v2) \ - zend_call_method_with_2_params(SW_Z8_OBJ_P(zobj), obj_ce, fn_ptr_ptr, fn_name, retval, v1, v2) - -static sw_inline int sw_zend_function_max_num_args(zend_function *function) -{ - // https://github.com/php/php-src/commit/2646f7bcb98dcdd322ea21701c8bb101104ea619 - // zend_function.common.num_args don't include the variadic argument anymore. - return (function->common.fn_flags & ZEND_ACC_VARIADIC) ? UINT32_MAX : function->common.num_args; -} - -// TODO: remove it after remove async modules -static sw_inline zend_bool sw_zend_is_callable(zval *callable, int check_flags, char **callable_name) -{ - zend_string *name; - zend_bool ret = zend_is_callable(callable, check_flags, &name); - *callable_name = estrndup(ZSTR_VAL(name), ZSTR_LEN(name)); - zend_string_release(name); - return ret; -} - -static sw_inline zend_bool sw_zend_is_callable_at_frame(zval *zcallable, zval *zobject, zend_execute_data *frame, uint check_flags, char **callable_name, 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 - if (callable_name) - { - *callable_name = estrndup(ZSTR_VAL(name), ZSTR_LEN(name)); - } - if (callable_name_len) - { - *callable_name_len = ZSTR_LEN(name); - } - zend_string_release(name); - return ret; -} - -static sw_inline zend_bool sw_zend_is_callable_ex(zval *zcallable, zval *zobject, uint check_flags, char **callable_name, size_t *callable_name_len, zend_fcall_info_cache *fci_cache, char **error) -{ - return sw_zend_is_callable_at_frame(zcallable, zobject, NULL, check_flags, callable_name, callable_name_len, fci_cache, error); -} - -/* this API can work well when retval is NULL */ -static sw_inline int sw_zend_call_function_ex(zval *function_name, zend_fcall_info_cache *fci_cache, uint32_t param_count, zval *params, zval *retval) -{ - zend_fcall_info fci; - zval _retval; - int ret; - - fci.size = sizeof(fci); - fci.object = NULL; - if (!fci_cache || !fci_cache->function_handler) - { - if (!function_name) - { - php_swoole_fatal_error(E_WARNING, "Bad function"); - return FAILURE; - } - ZVAL_COPY_VALUE(&fci.function_name, function_name); - } - else - { - ZVAL_UNDEF(&fci.function_name); - } - 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); - - if (!retval) - { - zval_ptr_dtor(&_retval); - } - return ret; -} - -/* we must check for exception immediately if we don't have chances to go back to ZendVM (e.g event loop) */ -static sw_inline int sw_zend_call_function_ex2(zval *function_name, zend_fcall_info_cache *fci_cache, uint32_t param_count, zval *params, zval *retval) -{ - int ret = sw_zend_call_function_ex(function_name, fci_cache, param_count, params, retval); - if (UNEXPECTED(EG(exception))) - { - zend_exception_error(EG(exception), E_ERROR); - } - return ret; -} - -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; - } - int ret = zend_call_function(fci, fci_cache); - if (fci->retval == &retval) - { - zval_ptr_dtor(&retval); - } - if (exception) - { - EG(exception) = exception; - } - return ret; -} - -static sw_inline void sw_zend_fci_params_persist(zend_fcall_info *fci) -{ - if (fci->param_count > 0) - { - uint32_t i; - zval *params = (zval *) ecalloc(fci->param_count, sizeof(zval)); - for (i = 0; i < fci->param_count; i++) - { - ZVAL_COPY(¶ms[i], &fci->params[i]); - } - fci->params = params; - } -} - -static sw_inline void sw_zend_fci_params_discard(zend_fcall_info *fci) -{ - if (fci->param_count > 0) - { - uint32_t i; - for (i = 0; i < fci->param_count; i++) - { - zval_ptr_dtor(&fci->params[i]); - } - efree(fci->params); - } -} - -static sw_inline void sw_zend_fci_cache_persist(zend_fcall_info_cache *fci_cache) -{ - if (fci_cache->object) - { - GC_ADDREF(fci_cache->object); - } - if (fci_cache->function_handler->op_array.fn_flags & ZEND_ACC_CLOSURE) - { - GC_ADDREF(ZEND_CLOSURE_OBJECT(fci_cache->function_handler)); - } -} - -static sw_inline void sw_zend_fci_cache_discard(zend_fcall_info_cache *fci_cache) -{ - if (fci_cache->object) - { - OBJ_RELEASE(fci_cache->object); - } - if (fci_cache->function_handler->op_array.fn_flags & ZEND_ACC_CLOSURE) - { - OBJ_RELEASE(ZEND_CLOSURE_OBJECT(fci_cache->function_handler)); - } -} - -/* 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); -} - -//----------------------------------Misc API------------------------------------ - -static sw_inline int php_swoole_check_reactor() -{ - if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_BEGIN) - { - return -1; - } - if (sw_unlikely(!sw_reactor())) - { - return php_swoole_reactor_init() == SW_OK ? 1 : -1; - } - else - { - return 0; - } -} - -static sw_inline char* php_swoole_format_date(char *format, size_t format_len, time_t ts, int localtime) -{ - zend_string *time = php_format_date(format, format_len, ts, localtime); - char *return_str = estrndup(ZSTR_VAL(time), ZSTR_LEN(time)); - zend_string_release(time); - return return_str; -} - -static sw_inline char* php_swoole_url_encode(const char *value, size_t value_len, int* exten) -{ - zend_string *str = php_url_encode(value, value_len); - *exten = ZSTR_LEN(str); - char *return_str = estrndup(ZSTR_VAL(str), ZSTR_LEN(str)); - zend_string_release(str); - return return_str; -} - -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)) { - php_url_encode_hash_ex(HASH_OF(zdata), formstr, NULL, 0, NULL, 0, NULL, 0, NULL, NULL, (int) PHP_QUERY_RFC1738); - } - else - { -#endif - if (formstr->s) - { - smart_str_free(formstr); - } - return NULL; - } - if (!formstr->s) - { - return NULL; - } - smart_str_0(formstr); - *length = formstr->s->len; - return formstr->s->val; -} - -END_EXTERN_C() - -#endif /* PHP_SWOOLE_H */ 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 1415c9ddd9a..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, @@ -117,6 +128,11 @@ class PHPCoroutine { HOOK_NATIVE_CURL = 1u << 12, 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 @@ -125,47 +141,62 @@ class PHPCoroutine { }; static const uint8_t MAX_EXEC_MSEC = 10; - static void init(); - static void deactivate(void *ptr); 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(active) ? Coroutine::get_current_cid() : -1; + 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) { - return sw_likely(active) ? Coroutine::get_elapsed(cid) : -1; + return sw_likely(activated) ? Coroutine::get_elapsed(cid) : -1; } 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() { @@ -180,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; @@ -213,36 +244,67 @@ class PHPCoroutine { } static inline bool is_activated() { - return active; + 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 active; - 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 185ae9f9e9e..f4a75506e87 100644 --- a/ext-src/php_swoole_coroutine_system.h +++ b/ext-src/php_swoole_coroutine_system.h @@ -19,84 +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_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 new file mode 100644 index 00000000000..3cd4ee67a54 --- /dev/null +++ b/ext-src/php_swoole_curl.h @@ -0,0 +1,177 @@ +/* + +----------------------------------------------------------------------+ + | 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 "php_swoole_cxx.h" + +#ifdef SW_USE_CURL +#include "swoole_util.h" + +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 + +#if LIBCURL_VERSION_NUM < 0x073800 +#error "require cURL version 7.56.0 or later" +#endif + +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 HandleSocket { + network::Socket *socket; + int event_bitmask; + int event_fd; + int action; +}; + +struct Handle { + CURL *cp; + Multi *multi; + std::unordered_map sockets; + + Handle(CURL *_cp) { + cp = _cp; + multi = nullptr; + } + + HandleSocket *create_socket(curl_socket_t sockfd); + void destroy_socket(curl_socket_t sockfd); +}; + +Handle *get_handle(CURL *cp); +Handle *create_handle(CURL *ch); +void destroy_handle(CURL *ch); + +struct Selector { + bool timer_callback = false; + std::set active_handles; +}; + +class Multi { + CURLM *multi_handle_; + TimerNode *timer = nullptr; + long timeout_ms_ = 0; + Coroutine *co = nullptr; + int running_handles_ = 0; + int last_sockfd; + int event_count_ = 0; + bool defer_callback = false; + std::unique_ptr selector; + + CURLcode read_info(); + + HandleSocket *create_socket(Handle *handle, 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) { + this->timer = nullptr; + callback(nullptr, 0); + }); + } + + void del_timer() { + if (timer && swoole_timer_is_available()) { + swoole_timer_del(timer); + timeout_ms_ = -1; + timer = nullptr; + } + } + + void set_timer() { + long _timeout_ms = 0; + curl_multi_timeout(multi_handle_, &_timeout_ms); + handle_timeout(multi_handle_, _timeout_ms, this); + } + + public: + Multi() { + multi_handle_ = curl_multi_init(); + co = nullptr; + curl_multi_setopt(multi_handle_, CURLMOPT_SOCKETFUNCTION, handle_socket); + curl_multi_setopt(multi_handle_, CURLMOPT_TIMERFUNCTION, handle_timeout); + curl_multi_setopt(multi_handle_, CURLMOPT_SOCKETDATA, this); + curl_multi_setopt(multi_handle_, CURLMOPT_TIMERDATA, this); + } + + ~Multi() { + del_timer(); + curl_multi_cleanup(multi_handle_); + } + + CURLM *get_multi_handle() { + return multi_handle_; + } + + int get_running_handles() { + return running_handles_; + } + + void set_selector(Selector *_selector) { + selector.reset(_selector); + } + + CURLMcode add_handle(Handle *handle); + CURLMcode remove_handle(Handle *handle); + + CURLMcode perform() { + return curl_multi_perform(multi_handle_, &running_handles_); + } + + int get_event(int action) { + return action == CURL_POLL_IN ? SW_EVENT_READ : SW_EVENT_WRITE; + } + + Coroutine *check_bound_co() { + if (co) { + swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, "cURL is executing, cannot be operated"); + return nullptr; + } + return Coroutine::get_current_safe(); + } + + CURLcode exec(Handle *handle); + long select(php_curlm *mh, double timeout = -1); + 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); + static int cb_error(Reactor *reactor, Event *event); + 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 b45a9d73f18..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,74 +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 include(const std::string &file) { - zend_file_handle file_handle; - int ret = php_stream_open_for_zend_ex(file.c_str(), &file_handle, USE_PATH | STREAM_OPEN_FOR_INCLUDE); - if (ret != SUCCESS) { - return false; - } - - zend_string *opened_path; - if (!file_handle.opened_path) { - file_handle.opened_path = zend_string_init(file.c_str(), file.length(), 0); - } - opened_path = zend_string_copy(file_handle.opened_path); - zval dummy; - - zval retval; - zend_op_array *new_op_array; - ZVAL_NULL(&dummy); - if (zend_hash_add(&EG(included_files), opened_path, &dummy)) { - new_op_array = zend_compile_file(&file_handle, ZEND_REQUIRE); - zend_destroy_file_handle(&file_handle); - } else { - new_op_array = nullptr; - zend_file_handle_dtor(&file_handle); - } - zend_string_release(opened_path); - if (!new_op_array) { - return false; - } - - zend_execute(new_op_array, &retval); - - destroy_op_array(new_op_array); - efree(new_op_array); - return Z_TYPE(retval) == IS_TRUE; -} - -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; @@ -96,10 +36,6 @@ void known_strings_dtor(void) { sw_zend_known_strings = nullptr; } -zend_string *fetch_zend_string_by_val(char *val) { - return (zend_string *) (val - XtOffsetOf(zend_string, val)); -} - namespace function { bool call(zend_fcall_info_cache *fci_cache, uint32_t argc, zval *argv, zval *retval, const bool enable_coroutine) { @@ -109,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; } @@ -120,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); } @@ -136,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 70ebeae2afa..c3b19b27ccb 100644 --- a/ext-src/php_swoole_cxx.h +++ b/ext-src/php_swoole_cxx.h @@ -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 | +----------------------------------------------------------------------+ */ #pragma once -#include "php_swoole.h" +#include "php_swoole_private.h" #include "php_swoole_coroutine.h" #include "swoole_util.h" @@ -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,66 +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 { @@ -177,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; } @@ -210,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; @@ -251,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); @@ -274,6 +350,7 @@ class ArrayIterator { _index = _ptr->h; pe = p; } + ArrayIterator(Bucket *p, Bucket *_pe) { _ptr = p; _key = _ptr->key; @@ -282,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; } @@ -340,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); @@ -389,46 +472,355 @@ 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); } }; -ReturnValue call(const std::string &func_name, int argc, zval *argv); +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_; + } +}; + +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 include(const std::string &file); -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); void json_decode(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long zend_long); -zend_string *fetch_zend_string_by_val(char *val); -#if PHP_VERSION_ID < 80000 -#define ZEND_STR_CONST -#else -#define ZEND_STR_CONST const -#endif +static inline zend_string *fetch_zend_string_by_val(void *val) { + return (zend_string *) ((char *) val - XtOffsetOf(zend_string, val)); +} + +static inline void assign_zend_string_by_val(zval *zdata, char *addr, size_t length) { + zend_string *zstr = fetch_zend_string_by_val(addr); + addr[length] = 0; + zstr->len = length; + ZVAL_STR(zdata, zstr); +} + +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 fa4df346673..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 { - -#ifdef SW_USE_HTTP2 +class Server; 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; @@ -119,15 +127,16 @@ struct Response { struct Context { SessionId fd; uchar completed : 1; - uchar end : 1; - uchar send_header : 1; + uchar end_ : 1; + uchar send_header_ : 1; #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; - uchar send_trailer : 1; + uchar send_trailer_ : 1; uchar keepalive : 1; uchar websocket : 1; #ifdef SW_HAVE_ZLIB @@ -139,15 +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; @@ -157,25 +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; - const char *upload_tmp_dir; + 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 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 + + void http2_end(zval *zdata, zval *return_value); + 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: @@ -183,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(swString *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); @@ -205,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; @@ -223,25 +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); -void swoole_http_context_free(swoole::http::Context *ctx); -void swoole_http_context_copy(swoole::http::Context *src, swoole::http::Context *dst); +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) { @@ -249,31 +352,36 @@ 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; } -int swoole_http_parse_form_data(swoole::http::Context *ctx, const char *boundary_str, int boundary_len); + +static inline bool swoole_http_has_crlf(const char *value, size_t length) { + /* new line/NUL character safety check */ + for (size_t i = 0; i < length; i++) { + /* RFC 7230 ch. 3.2.4 deprecates folding support */ + if (value[i] == '\n' || value[i] == '\r') { + php_swoole_error(E_WARNING, "Header may not contain more than a single header, new line detected"); + return true; + } + if (value[i] == '\0') { + php_swoole_error(E_WARNING, "Header may not contain NUL bytes"); + return true; + } + } + + return false; +} + 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); -size_t swoole_http_requset_parse(swoole::http::Context *ctx, const char *data, size_t length); - -bool swoole_http_response_set_header(swoole::http::Context *, const char *, size_t, zval *, bool); -bool swoole_http_response_set_header(swoole::http::Context *, const char *, size_t, const char *, size_t, bool); - -void swoole_http_response_end(swoole::http::Context *ctx, zval *zdata, zval *return_value); -void swoole_http_response_send_trailer(swoole::http::Context *ctx, zval *return_value); - -#ifdef SW_HAVE_COMPRESSION -int swoole_http_response_compress(const char *data, size_t length, int method, int level); -void swoole_http_get_compression_method(swoole::http::Context *ctx, const char *accept_encoding, size_t length); -const char *swoole_http_get_content_encoding(swoole::http::Context *ctx); -#endif #ifdef SW_HAVE_ZLIB voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size); @@ -285,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); }, @@ -296,8 +402,6 @@ static sw_inline nghttp2_mem *php_nghttp2_mem() { return &mem; } -void swoole_http2_response_end(swoole::http::Context *ctx, zval *zdata, zval *return_value); - namespace swoole { namespace http2 { //-----------------------------------namespace begin-------------------------------------------- @@ -327,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); @@ -374,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 9edb471280f..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,25 +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); -void swoole_http_server_init_context(swoole::Server *serv, 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 9c898e1a7e2..78cc7d011d0 100644 --- a/ext-src/php_swoole_library.h +++ b/ext-src/php_swoole_library.h @@ -1,8 +1,70 @@ /** + * ----------------------------------------------------------------------- * Generated by build-library.php, Please DO NOT modify! + +----------------------------------------------------------------------+ + | Swoole | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.0 of the Apache license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.apache.org/licenses/LICENSE-2.0.html | + | If you did not receive a copy of the Apache2.0 license and are unable| + | to obtain it through the world-wide-web, please send a note to | + | license@swoole.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ */ -/* $Id: 7420b0f61778714cb4f2b2e6d9baa70686fc41d0 */ +/* $Id: 55873a764b83b7993498004cb1d9d151ae446141 */ + +#ifndef SWOOLE_LIBRARY_H +#define SWOOLE_LIBRARY_H + +#if PHP_VERSION_ID < 80000 +typedef zval zend_source_string_t; +#else +typedef zend_string zend_source_string_t; +#endif + +#if PHP_VERSION_ID < 80200 +#define ZEND_COMPILE_POSITION_DC +#define ZEND_COMPILE_POSITION_RELAY_C +#else +#define ZEND_COMPILE_POSITION_DC , zend_compile_position position +#define ZEND_COMPILE_POSITION_RELAY_C , position +#endif + +#if PHP_VERSION_ID < 80000 +#define ZEND_STR_CONST +#else +#define ZEND_STR_CONST const +#endif + + +static zend_op_array *(*old_compile_string)(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC); + +static inline zend_op_array *_compile_string(zend_source_string_t *source_string, ZEND_STR_CONST char *filename ZEND_COMPILE_POSITION_DC) { + if (UNEXPECTED(EG(exception))) { + zend_exception_error(EG(exception), E_ERROR); + return NULL; + } + zend_op_array *opa = old_compile_string(source_string, filename ZEND_COMPILE_POSITION_RELAY_C); + opa->type = ZEND_USER_FUNCTION; + return opa; +} + +static inline zend_bool _eval(const char *code, const char *filename) { + if (!old_compile_string) { + old_compile_string = zend_compile_string; + } + // overwrite + zend_compile_string = _compile_string; + int ret = (zend_eval_stringl((char *) code, strlen(code), NULL, (char *) filename) == SUCCESS); + // recover + zend_compile_string = old_compile_string; + return ret; +} + +#endif static const char* swoole_library_source_constants = "\n" @@ -19,7 +81,9 @@ static const char* swoole_library_source_constants = "define('SWOOLE_LIBRARY', true);\n" "\n" "!defined('CURLOPT_HEADEROPT') && define('CURLOPT_HEADEROPT', 229);\n" - "!defined('CURLOPT_PROXYHEADER') && define('CURLOPT_PROXYHEADER', 10228);\n"; + "!defined('CURLOPT_PROXYHEADER') && define('CURLOPT_PROXYHEADER', 10228);\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" @@ -33,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" @@ -58,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" @@ -81,43 +147,60 @@ static const char* swoole_library_source_core_constant = "\n" "class Constant\n" "{\n" - " public const EVENT_RECEIVE = 'receive';\n" - "\n" - " public const EVENT_CONNECT = 'connect';\n" + " /* {{{ EVENT */\n" + " public const EVENT_START = 'start';\n" "\n" - " public const EVENT_CLOSE = 'close';\n" + " public const EVENT_BEFORE_SHUTDOWN = 'beforeShutdown';\n" "\n" - " public const EVENT_PACKET = 'packet';\n" + " public const EVENT_SHUTDOWN = 'shutdown';\n" "\n" - " public const EVENT_REQUEST = 'request';\n" + " public const EVENT_WORKER_START = 'workerStart';\n" "\n" - " public const EVENT_MESSAGE = 'message';\n" + " public const EVENT_WORKER_STOP = 'workerStop';\n" "\n" - " public const EVENT_OPEN = 'open';\n" + " public const EVENT_BEFORE_RELOAD = 'beforeReload';\n" "\n" - " public const EVENT_HANDSHAKE = 'handshake';\n" + " public const EVENT_AFTER_RELOAD = 'afterReload';\n" "\n" " public const EVENT_TASK = 'task';\n" "\n" " public const EVENT_FINISH = 'finish';\n" "\n" - " public const EVENT_START = 'start';\n" - "\n" - " public const EVENT_SHUTDOWN = 'shutdown';\n" - "\n" - " public const EVENT_WORKER_START = 'workerStart';\n" - "\n" " public const EVENT_WORKER_EXIT = 'workerExit';\n" "\n" " public const EVENT_WORKER_ERROR = 'workerError';\n" "\n" - " public const EVENT_WORKER_STOP = 'workerStop';\n" + " public const EVENT_MANAGER_START = 'managerStart';\n" + "\n" + " public const EVENT_MANAGER_STOP = 'managerStop';\n" "\n" " public const EVENT_PIPE_MESSAGE = 'pipeMessage';\n" "\n" - " public const EVENT_MANAGER_START = 'managerStart';\n" + " public const EVENT_CONNECT = 'connect';\n" "\n" - " public const EVENT_MANAGER_STOP = 'managerStop';\n" + " public const EVENT_RECEIVE = 'receive';\n" + "\n" + " public const EVENT_CLOSE = 'close';\n" + "\n" + " public const EVENT_PACKET = 'packet';\n" + "\n" + " public const EVENT_BUFFER_FULL = 'bufferFull';\n" + "\n" + " public const EVENT_BUFFER_EMPTY = 'bufferEmpty';\n" + "\n" + " public const EVENT_REQUEST = 'request';\n" + "\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" + "\n" + " public const EVENT_DISCONNECT = 'disconnect';\n" + "\n" + " /* }}} EVENT */\n" "\n" " public const EVENT_ERROR = 'error';\n" "\n" @@ -156,6 +239,41 @@ 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" + "\n" + " public const OPTION_AIO_MAX_WAIT_TIME = 'aio_max_wait_time';\n" + "\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" @@ -198,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" @@ -246,18 +366,12 @@ 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" + " public const OPTION_MAX_CORO_NUM = 'max_coro_num';\n" "\n" " public const OPTION_MAX_COROUTINE = 'max_coroutine';\n" "\n" + " public const OPTION_ENABLE_DEADLOCK_CHECK = 'enable_deadlock_check';\n" + "\n" " public const OPTION_HOOK_FLAGS = 'hook_flags';\n" "\n" " public const OPTION_ENABLE_PREEMPTIVE_SCHEDULER = 'enable_preemptive_scheduler';\n" @@ -266,19 +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_AIO_CORE_WORKER_NUM = 'aio_core_worker_num';\n" - "\n" - " public const OPTION_AIO_WORKER_NUM = 'aio_worker_num';\n" - "\n" - " public const OPTION_AIO_MAX_WAIT_TIME = 'aio_max_wait_time';\n" + " public const OPTION_CONNECT_TIMEOUT = 'connect_timeout';\n" "\n" - " public const OPTION_AIO_MAX_IDLE_TIME = 'aio_max_idle_time';\n" + " public const OPTION_TIMEOUT = 'timeout';\n" "\n" - " public const OPTION_RECONNECT = 'reconnect';\n" + " public const OPTION_MAX_RETRIES = 'max_retries';\n" "\n" " public const OPTION_DEFER = 'defer';\n" "\n" @@ -288,46 +400,46 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_WEBSOCKET_MASK = 'websocket_mask';\n" "\n" + " public const OPTION_HTTP_COMPRESSION = 'http_compression';\n" + "\n" + " public const OPTION_BODY_DECOMPRESSION = 'body_decompression';\n" + "\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" "\n" " public const OPTION_HTTP_PARSE_FILES = 'http_parse_files';\n" "\n" - " public const OPTION_HTTP_COMPRESSION = 'http_compression';\n" - "\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_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" @@ -344,7 +456,9 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MAX_QUEUED_BYTES = 'max_queued_bytes';\n" "\n" - " public const OPTION_MAX_CORO_NUM = 'max_coro_num';\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" @@ -364,6 +478,10 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_TASK_USE_OBJECT = 'task_use_object';\n" "\n" + " public const OPTION_TASK_OBJECT = 'task_object';\n" + "\n" + " public const OPTION_EVENT_OBJECT = 'event_object';\n" + "\n" " public const OPTION_TASK_ENABLE_COROUTINE = 'task_enable_coroutine';\n" "\n" " public const OPTION_TASK_WORKER_NUM = 'task_worker_num';\n" @@ -380,6 +498,8 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MAX_CONN = 'max_conn';\n" "\n" + " public const OPTION_START_SESSION_ID = 'start_session_id';\n" + "\n" " public const OPTION_HEARTBEAT_CHECK_INTERVAL = 'heartbeat_check_interval';\n" "\n" " public const OPTION_HEARTBEAT_IDLE_TIME = 'heartbeat_idle_time';\n" @@ -394,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" @@ -414,6 +536,16 @@ static const char* swoole_library_source_core_constant = "\n" " public const OPTION_MESSAGE_QUEUE_KEY = 'message_queue_key';\n" "\n" + " /**\n" + " * @since 6.0.0-beta\n" + " */\n" + " public const OPTION_BOOTSTRAP = 'bootstrap';\n" + "\n" + " /**\n" + " * @since 6.0.0-beta\n" + " */\n" + " public const OPTION_INIT_ARGUMENTS = 'init_arguments';\n" + "\n" " public const OPTION_BACKLOG = 'backlog';\n" "\n" " public const OPTION_KERNEL_SOCKET_RECV_BUFFER_SIZE = 'kernel_socket_recv_buffer_size';\n" @@ -458,17 +590,41 @@ 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" "\n" + " public const OPTION_SSL_SNI_CERTS = 'ssl_sni_certs';\n" + "\n" " public const OPTION_OPEN_SSL = 'open_ssl';\n" "\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 = @@ -485,19 +641,13 @@ static const char* swoole_library_source_core_string_object = "\n" "namespace Swoole;\n" "\n" - "class StringObject\n" + "class StringObject implements \\Stringable\n" "{\n" " /**\n" - " * @var string\n" - " */\n" - " protected $string;\n" - "\n" - " /**\n" " * StringObject constructor.\n" " */\n" - " public function __construct(string $string = '')\n" + " public function __construct(protected string $string = '')\n" " {\n" - " $this->string = $string;\n" " }\n" "\n" " public function __toString(): string\n" @@ -505,41 +655,39 @@ static const char* swoole_library_source_core_string_object = " return $this->string;\n" " }\n" "\n" + " public static function from(string $string = ''): static\n" + " {\n" + " return new static($string); // @phpstan-ignore new.static\n" + " }\n" + "\n" " public function length(): int\n" " {\n" " return strlen($this->string);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function indexOf(string $needle, int $offset = 0)\n" + " public function indexOf(string $needle, int $offset = 0): false|int\n" " {\n" - " return strpos($this->string, ...func_get_args());\n" + " return strpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function lastIndexOf(string $needle, int $offset = 0)\n" + " public function lastIndexOf(string $needle, int $offset = 0): false|int\n" " {\n" - " return strrpos($this->string, ...func_get_args());\n" + " return strrpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function pos(string $needle, int $offset = 0)\n" + " public function pos(string $needle, int $offset = 0): false|int\n" " {\n" - " return strpos($this->string, ...func_get_args());\n" + " return strpos($this->string, $needle, $offset);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function rpos(string $needle, int $offset = 0)\n" + " public function rpos(string $needle, int $offset = 0): false|int\n" + " {\n" + " return strrpos($this->string, $needle, $offset);\n" + " }\n" + "\n" + " public function reverse(): static\n" " {\n" - " return strrpos($this->string, ...func_get_args());\n" + " return new static(strrev($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -550,28 +698,22 @@ static const char* swoole_library_source_core_string_object = " return stripos($this->string, $needle);\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function lower(): self\n" + " public function lower(): static\n" " {\n" - " return new static(strtolower($this->string));\n" + " return new static(strtolower($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function upper(): self\n" + " public function upper(): static\n" " {\n" - " return new static(strtoupper($this->string));\n" + " return new static(strtoupper($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @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" @@ -579,7 +721,7 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function ltrim(): self\n" " {\n" - " return new static(ltrim($this->string));\n" + " return new static(ltrim($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -587,7 +729,7 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function rtrim(): self\n" " {\n" - " return new static(rtrim($this->string));\n" + " return new static(rtrim($this->string)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -595,39 +737,30 @@ static const char* swoole_library_source_core_string_object = " */\n" " public function substr(int $offset, ?int $length = null)\n" " {\n" - " return new static(substr($this->string, ...func_get_args()));\n" + " return new static(substr($this->string, $offset, $length)); // @phpstan-ignore new.static\n" " }\n" "\n" - " public function repeat(int $n): StringObject\n" + " public function repeat(int $times): static\n" " {\n" - " return new static(str_repeat($this->string, $n));\n" + " return new static(str_repeat($this->string, $times)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @param $str\n" - " */\n" - " public function append($str): StringObject\n" + " public function append(mixed $str): static\n" " {\n" - " if (is_string($str)) {\n" - " $this->string .= $str;\n" - " } else {\n" - " $this->string .= strval($str);\n" - " }\n" - " return $this;\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)\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" @@ -648,7 +781,7 @@ static const char* swoole_library_source_core_string_object = "\n" " public function contains(string $subString): bool\n" " {\n" - " return strpos($this->string, $subString) !== false;\n" + " return str_contains($this->string, $subString);\n" " }\n" "\n" " public function split(string $delimiter, int $limit = PHP_INT_MAX): ArrayObject\n" @@ -665,16 +798,26 @@ static const char* swoole_library_source_core_string_object = " }\n" "\n" " /**\n" - " * @return static\n" + " * Get a new string object by splitting the string of current object into smaller chunks.\n" + " *\n" + " * @param int $length The chunk length.\n" + " * @param string $separator The line ending sequence.\n" + " * @see https://www.php.net/chunk_split\n" " */\n" - " public function chunkSplit(int $chunkLength = 76, string $chunkEnd = '')\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" @@ -709,57 +852,46 @@ static const char* swoole_library_source_core_multibyte_string_object = " return mb_strlen($this->string);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function indexOf(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function indexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strpos($this->string, ...func_get_args());\n" + " return mb_strpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function lastIndexOf(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strrpos($this->string, ...func_get_args());\n" + " return mb_strrpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function pos(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function pos(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strpos($this->string, ...func_get_args());\n" + " return mb_strpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function rpos(string $needle, int $offset = 0, ?string $encoding = null)\n" + " public function rpos(string $needle, int $offset = 0, ?string $encoding = null): false|int\n" " {\n" - " return mb_strrpos($this->string, ...func_get_args());\n" + " return mb_strrpos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" - " /**\n" - " * @return false|int\n" - " */\n" - " public function ipos(string $needle, ?string $encoding = null)\n" + " public function ipos(string $needle, int $offset = 0, ?string $encoding = null): int|false\n" " {\n" - " return mb_stripos($this->string, ...func_get_args());\n" + " return mb_stripos($this->string, $needle, $offset, $encoding);\n" " }\n" "\n" " /**\n" - " * @return static\n" + " * @see https://www.php.net/mb_substr\n" " */\n" - " public function substr(int $offset, ?int $length = null, ?string $encoding = null)\n" + " public function substr(int $start, ?int $length = null, ?string $encoding = null): static\n" " {\n" - " return new static(mb_substr($this->string, ...func_get_args()));\n" + " return new static(mb_substr($this->string, $start, $length, $encoding)); // @phpstan-ignore new.static\n" " }\n" "\n" - " public function chunk(int $splitLength = 1, ?int $limit = null): ArrayObject\n" + " /**\n" + " * {@inheritDoc}\n" + " * @see https://www.php.net/mb_str_split\n" + " */\n" + " public function chunk(int $length = 1): ArrayObject\n" " {\n" - " return static::detectArrayType(mb_split($this->string, ...func_get_args()));\n" + " return static::detectArrayType(mb_str_split($this->string, $length));\n" " }\n" "}\n"; @@ -795,14 +927,9 @@ static const char* swoole_library_source_core_array_object = "\n" "namespace Swoole;\n" "\n" - "use ArrayAccess;\n" - "use Countable;\n" - "use Iterator;\n" - "use RuntimeException;\n" - "use Serializable;\n" "use Swoole\\Exception\\ArrayKeyNotExists;\n" "\n" - "class ArrayObject implements ArrayAccess, Serializable, Countable, Iterator\n" + "class ArrayObject implements \\ArrayAccess, \\Serializable, \\Countable, \\Iterator\n" "{\n" " /**\n" " * @var array\n" @@ -822,6 +949,21 @@ static const char* swoole_library_source_core_array_object = " return $this->array;\n" " }\n" "\n" + " public function __serialize(): array\n" + " {\n" + " return $this->array;\n" + " }\n" + "\n" + " public function __unserialize(array $data): void\n" + " {\n" + " $this->array = $data;\n" + " }\n" + "\n" + " public static function from(array $array = []): static\n" + " {\n" + " return new static($array); // @phpstan-ignore new.static\n" + " }\n" + "\n" " public function toArray(): array\n" " {\n" " return $this->array;\n" @@ -840,6 +982,7 @@ static const char* swoole_library_source_core_array_object = " /**\n" " * @return mixed\n" " */\n" + " #[\\ReturnTypeWillChange]\n" " public function current()\n" " {\n" " return current($this->array);\n" @@ -848,6 +991,7 @@ static const char* swoole_library_source_core_array_object = " /**\n" " * @return mixed\n" " */\n" + " #[\\ReturnTypeWillChange]\n" " public function key()\n" " {\n" " return key($this->array);\n" @@ -861,6 +1005,7 @@ static const char* swoole_library_source_core_array_object = " /**\n" " * @return mixed\n" " */\n" + " #[\\ReturnTypeWillChange]\n" " public function rewind()\n" " {\n" " return reset($this->array);\n" @@ -869,16 +1014,16 @@ static const char* swoole_library_source_core_array_object = " /**\n" " * @return mixed\n" " */\n" + " #[\\ReturnTypeWillChange]\n" " public function next()\n" " {\n" " return next($this->array);\n" " }\n" "\n" " /**\n" - " * @param mixed $key\n" " * @return ArrayObject|StringObject\n" " */\n" - " public function get($key)\n" + " public function get(mixed $key)\n" " {\n" " if (!$this->exists($key)) {\n" " throw new ArrayKeyNotExists($key);\n" @@ -887,6 +1032,17 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" + " * @return ArrayObject|StringObject\n" + " */\n" + " public function getOr(mixed $key, mixed $default = null)\n" + " {\n" + " if (!$this->exists($key)) {\n" + " return $default;\n" + " }\n" + " return static::detectType($this->array[$key]);\n" + " }\n" + "\n" + " /**\n" " * @return mixed\n" " */\n" " public function last()\n" @@ -899,7 +1055,7 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @return null|int|string\n" + " * @return int|string|null\n" " */\n" " public function firstKey()\n" " {\n" @@ -907,7 +1063,7 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @return null|int|string\n" + " * @return int|string|null\n" " */\n" " public function lastKey()\n" " {\n" @@ -927,31 +1083,27 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $key\n" - " * @param mixed $value\n" " * @return $this\n" " */\n" - " public function set($key, $value): self\n" + " public function set(mixed $key, mixed $value): self\n" " {\n" " $this->array[$key] = $value;\n" " return $this;\n" " }\n" "\n" " /**\n" - " * @param mixed $key\n" " * @return $this\n" " */\n" - " public function delete($key): self\n" + " public function delete(mixed $key): self\n" " {\n" " unset($this->array[$key]);\n" " return $this;\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return $this\n" " */\n" - " public function remove($value, bool $strict = true, bool $loop = false): self\n" + " public function remove(mixed $value, bool $strict = true, bool $loop = false): self\n" " {\n" " do {\n" " $key = $this->search($value, $strict);\n" @@ -974,10 +1126,10 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $key\n" - " * @return null|mixed\n" + " * @return mixed|null\n" " */\n" - " public function offsetGet($key)\n" + " #[\\ReturnTypeWillChange]\n" + " public function offsetGet(mixed $key)\n" " {\n" " if (!array_key_exists($key, $this->array)) {\n" " return null;\n" @@ -985,62 +1137,47 @@ static const char* swoole_library_source_core_array_object = " return $this->array[$key];\n" " }\n" "\n" - " /**\n" - " * @param mixed $key\n" - " * @param mixed $value\n" - " */\n" - " public function offsetSet($key, $value): void\n" + " public function offsetSet(mixed $key, mixed $value): void\n" " {\n" " $this->array[$key] = $value;\n" " }\n" "\n" - " /**\n" - " * @param mixed $key\n" - " */\n" - " public function offsetUnset($key): void\n" + " public function offsetUnset(mixed $key): void\n" " {\n" " unset($this->array[$key]);\n" " }\n" "\n" " /**\n" - " * @param mixed $key\n" " * @return bool\n" " */\n" - " public function offsetExists($key)\n" + " #[\\ReturnTypeWillChange]\n" + " public function offsetExists(mixed $key)\n" " {\n" " return isset($this->array[$key]);\n" " }\n" "\n" - " /**\n" - " * @param mixed $key\n" - " */\n" - " public function exists($key): bool\n" + " public function exists(mixed $key): bool\n" " {\n" " return array_key_exists($key, $this->array);\n" " }\n" "\n" - " /**\n" - " * @param mixed $value\n" - " */\n" - " public function contains($value, bool $strict = true): bool\n" + " public function contains(mixed $value, bool $strict = true): bool\n" " {\n" " return in_array($value, $this->array, $strict);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return mixed\n" " */\n" - " public function indexOf($value, bool $strict = true)\n" + " public function indexOf(mixed $value, bool $strict = true)\n" " {\n" " return $this->search($value, $strict);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return mixed\n" " */\n" - " public function lastIndexOf($value, bool $strict = true)\n" + " public function lastIndexOf(mixed $value, bool $strict = true)\n" " {\n" " $array = $this->array;\n" " for (end($array); ($currentKey = key($array)) !== null; prev($array)) {\n" @@ -1056,29 +1193,24 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $needle\n" " * @return mixed\n" " */\n" - " public function search($needle, bool $strict = true)\n" + " public function search(mixed $needle, bool $strict = true)\n" " {\n" " return array_search($needle, $this->array, $strict);\n" " }\n" "\n" " public function join(string $glue = ''): StringObject\n" " {\n" - " return static::detectStringType(implode($glue, $this->array));\n" + " return self::detectStringType(implode($glue, $this->array));\n" " }\n" "\n" - " public function serialize(): StringObject\n" + " public function serialize(): string\n" " {\n" - " return static::detectStringType(serialize($this->array));\n" + " return serialize($this->array);\n" " }\n" "\n" - " /**\n" - " * @param string $string\n" - " * @return $this\n" - " */\n" - " public function unserialize($string): self\n" + " public function unserialize(string|\\Stringable|StringObject $string): self\n" " {\n" " $this->array = (array) unserialize((string) $string);\n" " return $this;\n" @@ -1101,19 +1233,17 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return int\n" " */\n" - " public function push($value)\n" + " public function push(mixed $value)\n" " {\n" " return $this->pushBack($value);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return int\n" " */\n" - " public function pushFront($value)\n" + " public function pushFront(mixed $value)\n" " {\n" " return array_unshift($this->array, $value);\n" " }\n" @@ -1125,19 +1255,17 @@ static const char* swoole_library_source_core_array_object = " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return int\n" " */\n" - " public function pushBack($value)\n" + " public function pushBack(mixed $value)\n" " {\n" " return array_push($this->array, $value);\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return $this\n" " */\n" - " public function insert(int $offset, $value): self\n" + " public function insert(int $offset, mixed $value): self\n" " {\n" " if (is_array($value) || is_object($value) || is_null($value)) {\n" " $value = [$value];\n" @@ -1170,14 +1298,9 @@ static const char* swoole_library_source_core_array_object = " return array_pop($this->array);\n" " }\n" "\n" - " /**\n" - " * @param mixed $offset\n" - " * @param int $length\n" - " * @return static\n" - " */\n" - " public function slice($offset, int $length = null, bool $preserve_keys = false): self\n" + " public function slice(int $offset, ?int $length = null, bool $preserve_keys = false): static\n" " {\n" - " return new static(array_slice($this->array, ...func_get_args()));\n" + " return new static(array_slice($this->array, $offset, $length, $preserve_keys)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -1188,24 +1311,19 @@ static const char* swoole_library_source_core_array_object = " return static::detectType($this->array[array_rand($this->array, 1)]);\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function each(callable $fn): self\n" " {\n" - " if (array_walk($this->array, $fn) === false) {\n" - " throw new RuntimeException('array_walk() failed');\n" - " }\n" + " array_walk($this->array, $fn);\n" + "\n" " return $this;\n" " }\n" "\n" " /**\n" " * @param array $args\n" - " * @return static\n" " */\n" - " public function map(callable $fn, ...$args): self\n" + " public function map(callable $fn, ...$args): static\n" " {\n" - " return new static(array_map($fn, $this->array, ...$args));\n" + " return new static(array_map($fn, $this->array, ...$args)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" @@ -1219,76 +1337,54 @@ static const char* swoole_library_source_core_array_object = "\n" " /**\n" " * @param array $args\n" - " * @return static\n" " */\n" - " public function keys(...$args): self\n" + " public function keys(...$args): static\n" " {\n" - " return new static(array_keys($this->array, ...$args));\n" + " return new static(array_keys($this->array, ...$args)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function values(): self\n" + " public function values(): static\n" " {\n" - " return new static(array_values($this->array));\n" + " return new static(array_values($this->array)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @param mixed $column_key\n" - " * @param mixed $index\n" - " * @return static\n" - " */\n" - " public function column($column_key, $index = null): self\n" + " public function column(mixed $column_key, mixed $index = null): static\n" " {\n" - " return new static(array_column($this->array, $column_key, $index));\n" + " return new static(array_column($this->array, $column_key, $index)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function unique(int $sort_flags = SORT_STRING): self\n" + " public function unique(int $sort_flags = SORT_STRING): static\n" " {\n" - " return new static(array_unique($this->array, $sort_flags));\n" + " return new static(array_unique($this->array, $sort_flags)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function reverse(bool $preserve_keys = false): self\n" + " public function reverse(bool $preserve_keys = false): static\n" " {\n" - " return new static(array_reverse($this->array, $preserve_keys));\n" + " return new static(array_reverse($this->array, $preserve_keys)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function chunk(int $size, bool $preserve_keys = false): self\n" + " public function chunk(int $size, bool $preserve_keys = false): static\n" " {\n" - " return new static(array_chunk($this->array, $size, $preserve_keys));\n" + " return new static(array_chunk($this->array, $size, $preserve_keys)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" " * Swap keys and values in an array.\n" - " * @return static\n" " */\n" - " public function flip(): self\n" + " public function flip(): static\n" " {\n" - " return new static(array_flip($this->array));\n" + " return new static(array_flip($this->array)); // @phpstan-ignore new.static\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " public function filter(callable $fn, int $flag = 0): self\n" + " public function filter(callable $fn, int $flag = 0): static\n" " {\n" - " return new static(array_filter($this->array, $fn, $flag));\n" + " return new static(array_filter($this->array, $fn, $flag)); // @phpstan-ignore new.static\n" " }\n" "\n" " /**\n" " * | Function name | Sorts by | Maintains key association | Order of sort | Related functions |\n" " * | :---------------- | :------- | :-------------------------- | :-------------------------- | :---------------- |\n" - " * | array_multisort() | value | associative yes, numeric no | first array or sort options | array_walk() |\n" + " * | array_multisort() | value | associative yes, numeric no | first array or sort options | array_walk() |\n" " * | asort() | value | yes | low to high | arsort() |\n" " * | arsort() | value | yes | high to low | asort() |\n" " * | krsort() | key | yes | high to low | ksort() |\n" @@ -1296,11 +1392,11 @@ static const char* swoole_library_source_core_array_object = " * | natcasesort() | value | yes | natural, case insensitive | natsort() |\n" " * | natsort() | value | yes | natural | natcasesort() |\n" " * | rsort() | value | no | high to low | sort() |\n" - " * | shuffle() | value | no | random | array_rand() |\n" + " * | shuffle() | value | no | random | array_rand() |\n" " * | sort() | value | no | low to high | rsort() |\n" - " * | uasort() | value | yes | user defined | uksort() |\n" - " * | uksort() | key | yes | user defined | uasort() |\n" - " * | usort() | value | no | user defined | uasort() |\n" + " * | uasort() | value | yes | user defined | uksort() |\n" + " * | uksort() | key | yes | user defined | uasort() |\n" + " * | usort() | value | no | user defined | uasort() |\n" " */\n" "\n" " /**\n" @@ -1308,42 +1404,29 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function asort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (asort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('asort() failed');\n" - " }\n" + " asort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function arsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (arsort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('arsort() failed');\n" - " }\n" + " arsort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function krsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (krsort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('krsort() failed');\n" - " }\n" + " krsort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function ksort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (ksort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('ksort() failed');\n" - " }\n" + " ksort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" @@ -1352,8 +1435,8 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function natcasesort(): self\n" " {\n" - " if (natcasesort($this->array) !== true) {\n" - " throw new RuntimeException('natcasesort() failed');\n" + " if (natcasesort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" + " throw new \\RuntimeException('natcasesort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1363,8 +1446,8 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function natsort(): self\n" " {\n" - " if (natsort($this->array) !== true) {\n" - " throw new RuntimeException('natsort() failed');\n" + " if (natsort($this->array) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" + " throw new \\RuntimeException('natsort() failed');\n" " }\n" " return $this;\n" " }\n" @@ -1374,72 +1457,51 @@ static const char* swoole_library_source_core_array_object = " */\n" " public function rsort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (rsort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('rsort() failed');\n" + " if (rsort($this->array, $sort_flags) !== true) { // @phpstan-ignore notIdentical.alwaysFalse\n" + " throw new \\RuntimeException('rsort() failed');\n" " }\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function shuffle(): self\n" " {\n" - " if (shuffle($this->array) !== true) {\n" - " throw new RuntimeException('shuffle() failed');\n" - " }\n" + " shuffle($this->array);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function sort(int $sort_flags = SORT_REGULAR): self\n" " {\n" - " if (sort($this->array, $sort_flags) !== true) {\n" - " throw new RuntimeException('sort() failed');\n" - " }\n" + " sort($this->array, $sort_flags);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function uasort(callable $value_compare_func): self\n" " {\n" - " if (uasort($this->array, $value_compare_func) !== true) {\n" - " throw new RuntimeException('uasort() failed');\n" - " }\n" + " uasort($this->array, $value_compare_func);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function uksort(callable $value_compare_func): self\n" " {\n" - " if (uksort($this->array, $value_compare_func) !== true) {\n" - " throw new RuntimeException('uksort() failed');\n" - " }\n" + " uksort($this->array, $value_compare_func);\n" + "\n" " return $this;\n" " }\n" "\n" - " /**\n" - " * @return $this\n" - " */\n" " public function usort(callable $value_compare_func): self\n" " {\n" - " if (usort($this->array, $value_compare_func) !== true) {\n" - " throw new RuntimeException('usort() failed');\n" - " }\n" + " usort($this->array, $value_compare_func);\n" + "\n" " return $this;\n" " }\n" "\n" " /**\n" - " * @param mixed $value\n" " * @return ArrayObject|mixed|StringObject\n" " */\n" - " protected static function detectType($value)\n" + " protected static function detectType(mixed $value)\n" " {\n" " if (is_string($value)) {\n" " return static::detectStringType($value);\n" @@ -1455,12 +1517,9 @@ static const char* swoole_library_source_core_array_object = " return new StringObject($value);\n" " }\n" "\n" - " /**\n" - " * @return static\n" - " */\n" - " protected static function detectArrayType(array $value): self\n" + " protected static function detectArrayType(array $value): static\n" " {\n" - " return new static($value);\n" + " return new static($value); // @phpstan-ignore new.static\n" " }\n" "}\n"; @@ -1478,18 +1537,13 @@ static const char* swoole_library_source_core_object_proxy = "\n" "namespace Swoole;\n" "\n" - "use TypeError;\n" - "\n" "class ObjectProxy\n" "{\n" " /** @var object */\n" " protected $__object;\n" "\n" - " public function __construct($object)\n" + " public function __construct(object $object)\n" " {\n" - " if (!is_object($object)) {\n" - " throw new TypeError('Non-object given');\n" - " }\n" " $this->__object = $object;\n" " }\n" "\n" @@ -1545,16 +1599,13 @@ static const char* swoole_library_source_core_coroutine_wait_group = "\n" "namespace Swoole\\Coroutine;\n" "\n" - "use BadMethodCallException;\n" - "use InvalidArgumentException;\n" - "\n" "class WaitGroup\n" "{\n" - " protected $chan;\n" + " protected Channel $chan;\n" "\n" - " protected $count = 0;\n" + " protected int $count = 0;\n" "\n" - " protected $waiting = false;\n" + " protected bool $waiting = false;\n" "\n" " public function __construct(int $delta = 0)\n" " {\n" @@ -1567,11 +1618,11 @@ static const char* swoole_library_source_core_coroutine_wait_group = " public function add(int $delta = 1): void\n" " {\n" " if ($this->waiting) {\n" - " throw new BadMethodCallException('WaitGroup misuse: add called concurrently with wait');\n" + " throw new \\BadMethodCallException('WaitGroup misuse: add called concurrently with wait');\n" " }\n" " $count = $this->count + $delta;\n" " if ($count < 0) {\n" - " throw new InvalidArgumentException('WaitGroup misuse: negative counter');\n" + " throw new \\InvalidArgumentException('WaitGroup misuse: negative counter');\n" " }\n" " $this->count = $count;\n" " }\n" @@ -1580,7 +1631,7 @@ static const char* swoole_library_source_core_coroutine_wait_group = " {\n" " $count = $this->count - 1;\n" " if ($count < 0) {\n" - " throw new BadMethodCallException('WaitGroup misuse: negative counter');\n" + " throw new \\BadMethodCallException('WaitGroup misuse: negative counter');\n" " }\n" " $this->count = $count;\n" " if ($count === 0 && $this->waiting) {\n" @@ -1591,16 +1642,21 @@ 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" " return true;\n" " }\n" + "\n" + " public function count(): int\n" + " {\n" + " return $this->count;\n" + " }\n" "}\n"; static const char* swoole_library_source_core_coroutine_server = @@ -1617,13 +1673,11 @@ static const char* swoole_library_source_core_coroutine_server = "\n" "namespace Swoole\\Coroutine;\n" "\n" + "use Swoole\\Constant;\n" "use Swoole\\Coroutine;\n" "use Swoole\\Coroutine\\Server\\Connection;\n" "use Swoole\\Exception;\n" "\n" - "/* compatibility constant */\n" - "define('SWOOLE_COROUTINE_SOCKET_HAVE_SSL_HANDSHAKE', method_exists(Socket::class, 'sslHandshake'));\n" - "\n" "class Server\n" "{\n" " /** @var string */\n" @@ -1647,7 +1701,7 @@ static const char* swoole_library_source_core_coroutine_server = " /** @var bool */\n" " protected $running = false;\n" "\n" - " /** @var null|callable */\n" + " /** @var callable|null */\n" " protected $fn;\n" "\n" " /** @var Socket */\n" @@ -1663,7 +1717,7 @@ static const char* swoole_library_source_core_coroutine_server = " if ($_host->contains('::')) {\n" " $this->type = AF_INET6;\n" " } elseif ($_host->startsWith('unix:/')) {\n" - " $host = $_host->substr(5)->__toString();\n" + " $host = $_host->substr(5)->__toString();\n" " $this->type = AF_UNIX;\n" " } else {\n" " $this->type = AF_INET;\n" @@ -1680,9 +1734,9 @@ static const char* swoole_library_source_core_coroutine_server = " if (!$socket->listen()) {\n" " throw new Exception('listen() failed', $socket->errCode);\n" " }\n" - " $this->port = $socket->getsockname()['port'] ?? 0;\n" - " $this->fd = $socket->fd;\n" - " $this->socket = $socket;\n" + " $this->port = $socket->getsockname()['port'] ?? 0;\n" + " $this->fd = $socket->fd;\n" + " $this->socket = $socket;\n" " $this->setting['open_ssl'] = $ssl;\n" " }\n" "\n" @@ -1715,13 +1769,13 @@ static const char* swoole_library_source_core_coroutine_server = " return false;\n" " }\n" "\n" - " while ($this->running) {\n" - " /** @var Socket $conn */\n" + " while ($this->running) { // @phpstan-ignore while.alwaysTrue\n" " $conn = null;\n" + " /** @var Socket $conn */\n" " $conn = $socket->accept();\n" - " if ($conn) {\n" + " if ($conn) { // @phpstan-ignore if.alwaysTrue\n" " $conn->setProtocol($this->setting);\n" - " if (SWOOLE_COROUTINE_SOCKET_HAVE_SSL_HANDSHAKE && $this->setting['open_ssl'] ?? false) {\n" + " if (!empty($this->setting[Constant::OPTION_OPEN_SSL])) {\n" " $fn = static function ($fn, $connection) {\n" " /* @var $connection Connection */\n" " if (!$connection->exportSocket()->sslHandshake()) {\n" @@ -1731,7 +1785,7 @@ static const char* swoole_library_source_core_coroutine_server = " };\n" " $arguments = [$this->fn, new Connection($conn)];\n" " } else {\n" - " $fn = $this->fn;\n" + " $fn = $this->fn;\n" " $arguments = [new Connection($conn)];\n" " }\n" " if (Coroutine::create($fn, ...$arguments) < 0) {\n" @@ -1754,7 +1808,7 @@ static const char* swoole_library_source_core_coroutine_server = " }\n" " }\n" "\n" - " return true;\n" + " return true; // @phpstan-ignore deadCode.unreachable\n" " }\n" "}\n"; @@ -1824,44 +1878,44 @@ 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" - " if ($this->timer != -1) {\n" + " if ($this->timer !== -1) {\n" " Timer::clear($this->timer);\n" - " if (isset(static::$cancel_list[$this->cid])) {\n" - " unset(static::$cancel_list[$this->cid]);\n" + " if (isset(self::$cancel_list[$this->cid])) {\n" + " unset(self::$cancel_list[$this->cid]);\n" " return;\n" " }\n" " }\n" - " if ($this->cid != -1 && $this->cid != Coroutine::getCid()) {\n" + " if ($this->cid !== -1 && $this->cid !== Coroutine::getCid()) {\n" " Coroutine::resume($this->cid);\n" " } else {\n" - " static::$cancel_list[$this->cid] = true;\n" + " self::$cancel_list[$this->cid] = true;\n" " }\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" + " 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 = intval($timeout * 1000)) > 0) {\n" + " if ($timeout > 0 && ($timeout_ms = (int) ($timeout * 1000)) > 0) {\n" " $barrier->timer = Timer::after($timeout_ms, function () use ($cid) {\n" " self::$cancel_list[$cid] = true;\n" " Coroutine::resume($cid);\n" @@ -1870,8 +1924,270 @@ static const char* swoole_library_source_core_coroutine_barrier = " $barrier = null;\n" " if (!isset(self::$cancel_list[$cid])) {\n" " Coroutine::yield();\n" + " } else {\n" + " unset(self::$cancel_list[$cid]);\n" + " }\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_http_client_proxy = + "\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" + "class ClientProxy\n" + "{\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" + " $client = new Client($info['host'], swoole_array_default_value($info, 'port', 443), true);\n" + " } else {\n" + " throw new Exception('unknown scheme \"' . $info['scheme'] . '\"');\n" + " }\n" + " $client->setMethod($method);\n" + " if ($data) {\n" + " $client->setData($data);\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 new ClientProxy(\n" + " $client->getBody(),\n" + " $client->getStatusCode(),\n" + " $client->getHeaders() ?: [],\n" + " $client->getCookies() ?: []\n" + " );\n" + " }\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" + " * @throws Exception\n" + " */\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" + " */\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"; static const char* swoole_library_source_core_connection_pool = @@ -1888,35 +2204,25 @@ static const char* swoole_library_source_core_connection_pool = "\n" "namespace Swoole;\n" "\n" - "use RuntimeException;\n" "use Swoole\\Coroutine\\Channel;\n" - "use Throwable;\n" "\n" "class ConnectionPool\n" "{\n" " public const DEFAULT_SIZE = 64;\n" "\n" - " /** @var Channel */\n" - " protected $pool;\n" + " protected ?Channel $pool;\n" "\n" " /** @var callable */\n" " protected $constructor;\n" "\n" - " /** @var int */\n" - " protected $size;\n" - "\n" - " /** @var int */\n" - " protected $num;\n" + " protected int $size;\n" "\n" - " /** @var null|string */\n" - " protected $proxy;\n" + " protected int $num = 0;\n" "\n" - " public function __construct(callable $constructor, int $size = self::DEFAULT_SIZE, ?string $proxy = null)\n" + " public function __construct(callable $constructor, int $size = self::DEFAULT_SIZE, protected ?string $proxy = null)\n" " {\n" - " $this->pool = new Channel($this->size = $size);\n" + " $this->pool = new Channel($this->size = $size);\n" " $this->constructor = $constructor;\n" - " $this->num = 0;\n" - " $this->proxy = $proxy;\n" " }\n" "\n" " public function fill(): void\n" @@ -1926,15 +2232,21 @@ static const char* swoole_library_source_core_connection_pool = " }\n" " }\n" "\n" - " public function get()\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" " }\n" - " return $this->pool->pop();\n" + " return $this->pool->pop($timeout);\n" " }\n" "\n" " public function put($connection): void\n" @@ -1955,7 +2267,7 @@ static const char* swoole_library_source_core_connection_pool = " {\n" " $this->pool->close();\n" " $this->pool = null;\n" - " $this->num = 0;\n" + " $this->num = 0;\n" " }\n" "\n" " protected function make(): void\n" @@ -1966,9 +2278,9 @@ static const char* swoole_library_source_core_connection_pool = " $connection = new $this->proxy($this->constructor);\n" " } else {\n" " $constructor = $this->constructor;\n" - " $connection = $constructor();\n" + " $connection = $constructor();\n" " }\n" - " } catch (Throwable $throwable) {\n" + " } catch (\\Throwable $throwable) {\n" " $this->num--;\n" " throw $throwable;\n" " }\n" @@ -1990,13 +2302,11 @@ static const char* swoole_library_source_core_database_object_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use Error;\n" - "\n" "class ObjectProxy extends \\Swoole\\ObjectProxy\n" "{\n" - " public function __clone()\n" + " final public function __clone(): void\n" " {\n" - " throw new Error('Trying to clone an uncloneable database proxy object');\n" + " throw new \\Error('Trying to clone an uncloneable database proxy object');\n" " }\n" "}\n"; @@ -2016,36 +2326,28 @@ static const char* swoole_library_source_core_database_mysqli_config = "\n" "class MysqliConfig\n" "{\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" + " protected string $host = '127.0.0.1';\n" "\n" - " /** @var int */\n" - " protected $port = 3306;\n" + " protected int $port = 3306;\n" "\n" - " /** @var null|string */\n" - " protected $unixSocket = '';\n" + " protected ?string $unixSocket;\n" "\n" - " /** @var string */\n" - " protected $dbname = 'test';\n" + " protected string $dbname = 'test';\n" "\n" - " /** @var string */\n" - " protected $charset = 'utf8mb4';\n" + " protected string $charset = 'utf8mb4';\n" "\n" - " /** @var string */\n" - " protected $username = 'root';\n" + " protected string $username = 'root';\n" "\n" - " /** @var string */\n" - " protected $password = 'root';\n" + " protected string $password = 'root';\n" "\n" - " /** @var array */\n" - " protected $options = [];\n" + " protected array $options = [];\n" "\n" " public function getHost(): string\n" " {\n" " return $this->host;\n" " }\n" "\n" - " public function withHost($host): self\n" + " public function withHost(string $host): self\n" " {\n" " $this->host = $host;\n" " return $this;\n" @@ -2056,9 +2358,9 @@ static const char* swoole_library_source_core_database_mysqli_config = " return $this->port;\n" " }\n" "\n" - " public function getUnixSocket(): string\n" + " public function getUnixSocket(): ?string\n" " {\n" - " return $this->unixSocket;\n" + " return $this->unixSocket ?? null;\n" " }\n" "\n" " public function withUnixSocket(?string $unixSocket): self\n" @@ -2143,9 +2445,7 @@ static const char* swoole_library_source_core_database_mysqli_exception = "\n" "namespace Swoole\\Database;\n" "\n" - "use Exception;\n" - "\n" - "class MysqliException extends Exception\n" + "class MysqliException extends \\Exception\n" "{\n" "}\n"; @@ -2167,19 +2467,15 @@ static const char* swoole_library_source_core_database_mysqli_pool = "use Swoole\\ConnectionPool;\n" "\n" "/**\n" - " * @method mysqli|MysqliProxy get()\n" + " * @method \\mysqli|MysqliProxy get()\n" " * @method void put(mysqli|MysqliProxy $connection)\n" " */\n" "class MysqliPool extends ConnectionPool\n" "{\n" - " /** @var MysqliConfig */\n" - " protected $config;\n" - "\n" - " public function __construct(MysqliConfig $config, int $size = self::DEFAULT_SIZE)\n" + " public function __construct(protected MysqliConfig $config, int $size = self::DEFAULT_SIZE)\n" " {\n" - " $this->config = $config;\n" " parent::__construct(function () {\n" - " $mysqli = new mysqli();\n" + " $mysqli = new \\mysqli();\n" " foreach ($this->config->getOptions() as $option => $value) {\n" " $mysqli->set_opt($option, $value);\n" " }\n" @@ -2194,6 +2490,7 @@ static const char* swoole_library_source_core_database_mysqli_pool = " if ($mysqli->connect_errno) {\n" " throw new MysqliException($mysqli->connect_error, $mysqli->connect_errno);\n" " }\n" + " $mysqli->set_charset($this->config->getCharset());\n" " return $mysqli;\n" " }, $size, MysqliProxy::class);\n" " }\n" @@ -2213,8 +2510,9 @@ static const char* swoole_library_source_core_database_mysqli_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use mysqli;\n" - "\n" + "/**\n" + " * @method \\mysqli __getObject()\n" + " */\n" "class MysqliProxy extends ObjectProxy\n" "{\n" " public const IO_METHOD_REGEX = '/^autocommit|begin_transaction|change_user|close|commit|kill|multi_query|ping|prepare|query|real_connect|real_query|reap_async_query|refresh|release_savepoint|rollback|savepoint|select_db|send_query|set_charset|ssl_set$/i';\n" @@ -2225,23 +2523,19 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " 2013, // MYSQLND_CR_SERVER_LOST\n" " ];\n" "\n" - " /** @var mysqli */\n" + " /** @var \\mysqli */\n" " protected $__object;\n" "\n" - " /** @var string */\n" - " protected $charsetContext;\n" + " protected string $charsetContext;\n" "\n" - " /** @var null|array */\n" - " protected $setOptContext;\n" + " protected array $setOptContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $changeUserContext;\n" + " protected array $changeUserContext;\n" "\n" " /** @var callable */\n" " protected $constructor;\n" "\n" - " /** @var int */\n" - " protected $round = 0;\n" + " protected int $round = 0;\n" "\n" " public function __construct(callable $constructor)\n" " {\n" @@ -2259,10 +2553,7 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " break;\n" " }\n" " /* no more chances or non-IO failures */\n" - " if (\n" - " !in_array($this->__object->errno, static::IO_ERRORS, true) ||\n" - " $n === 0\n" - " ) {\n" + " if (!in_array($this->__object->errno, static::IO_ERRORS, true) || ($n === 0)) {\n" " throw new MysqliException($this->__object->error, $this->__object->errno);\n" " }\n" " $this->reconnect();\n" @@ -2290,15 +2581,13 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " parent::__construct($constructor());\n" " $this->round++;\n" " /* restore context */\n" - " if ($this->charsetContext) {\n" + " if (!empty($this->charsetContext)) {\n" " $this->__object->set_charset($this->charsetContext);\n" " }\n" - " if ($this->setOptContext) {\n" - " foreach ($this->setOptContext as $opt => $val) {\n" - " $this->__object->set_opt($opt, $val);\n" - " }\n" + " foreach ($this->setOptContext as $opt => $val) {\n" + " $this->__object->set_opt($opt, $val);\n" " }\n" - " if ($this->changeUserContext) {\n" + " if (!empty($this->changeUserContext)) {\n" " $this->__object->change_user(...$this->changeUserContext);\n" " }\n" " }\n" @@ -2320,7 +2609,7 @@ static const char* swoole_library_source_core_database_mysqli_proxy = " return $this->__object->set_charset($charset);\n" " }\n" "\n" - " public function change_user(string $user, string $password, string $database): bool\n" + " public function change_user(string $user, string $password, ?string $database): bool\n" " {\n" " $this->changeUserContext = [$user, $password, $database];\n" " return $this->__object->change_user($user, $password, $database);\n" @@ -2341,39 +2630,30 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use mysqli;\n" - "use mysqli_stmt;\n" - "\n" "class MysqliStatementProxy extends ObjectProxy\n" "{\n" " public const IO_METHOD_REGEX = '/^close|execute|fetch|prepare$/i';\n" "\n" - " /** @var mysqli_stmt */\n" + " /** @var \\mysqli_stmt */\n" " protected $__object;\n" "\n" - " /** @var null|string */\n" - " protected $queryString;\n" + " protected ?string $queryString;\n" "\n" - " /** @var null|array */\n" - " protected $attrSetContext;\n" + " protected array $attrSetContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $bindParamContext;\n" + " protected array $bindParamContext;\n" "\n" - " /** @var null|array */\n" - " protected $bindResultContext;\n" + " protected array $bindResultContext;\n" "\n" - " /** @var Mysqli|MysqliProxy */\n" - " protected $parent;\n" + " protected MysqliProxy $parent;\n" "\n" - " /** @var int */\n" - " protected $parentRound;\n" + " protected int $parentRound;\n" "\n" - " public function __construct(mysqli_stmt $object, ?string $queryString, MysqliProxy $parent)\n" + " public function __construct(\\mysqli_stmt $object, ?string $queryString, MysqliProxy $parent)\n" " {\n" " parent::__construct($object);\n" " $this->queryString = $queryString;\n" - " $this->parent = $parent;\n" + " $this->parent = $parent;\n" " $this->parentRound = $parent->getRound();\n" " }\n" "\n" @@ -2387,31 +2667,26 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " break;\n" " }\n" " /* no more chances or non-IO failures or in transaction */\n" - " if (\n" - " !in_array($this->__object->errno, $this->parent::IO_ERRORS, true) ||\n" - " $n === 0\n" - " ) {\n" + " if (!in_array($this->__object->errno, $this->parent::IO_ERRORS, true) || ($n === 0)) {\n" " throw new MysqliException($this->__object->error, $this->__object->errno);\n" " }\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 = $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" @@ -2443,6 +2718,93 @@ static const char* swoole_library_source_core_database_mysqli_statement_proxy = " }\n" "}\n"; +static const char* swoole_library_source_core_database_detects_lost_connections = + "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Database;\n" + "\n" + "class DetectsLostConnections\n" + "{\n" + " /**\n" + " * @var array\n" + " */\n" + " private const ERROR_MESSAGES = [\n" + " 'server has gone away',\n" + " 'no connection to the server',\n" + " 'Lost connection',\n" + " 'is dead or not enabled',\n" + " 'Error while sending',\n" + " 'decryption failed or bad record mac',\n" + " 'server closed the connection unexpectedly',\n" + " 'SSL connection has been closed unexpectedly',\n" + " 'Error writing data to the connection',\n" + " 'Resource deadlock avoided',\n" + " 'Transaction() on null',\n" + " 'child connection forced to terminate due to client_idle_limit',\n" + " 'query_wait_timeout',\n" + " 'reset by peer',\n" + " 'Physical connection is not usable',\n" + " 'TCP Provider: Error code 0x68',\n" + " 'ORA-03113',\n" + " 'ORA-03114',\n" + " 'Packets out of order. Expected',\n" + " 'Adaptive Server connection failed',\n" + " 'Communication link failure',\n" + " 'connection is no longer usable',\n" + " 'Login timeout expired',\n" + " 'SQLSTATE[HY000] [2002] Connection refused',\n" + " 'running with the --read-only option so it cannot execute this statement',\n" + " 'The connection is broken and recovery is not possible. The connection is marked by the client driver as unrecoverable. No attempt was made to restore the connection.',\n" + " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Try again',\n" + " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known',\n" + " 'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo for',\n" + " 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: EOF detected',\n" + " 'SQLSTATE[HY000]: General error: 1105 The last transaction was aborted due to Seamless Scaling. Please retry.',\n" + " 'Temporary failure in name resolution',\n" + " 'SQLSTATE[08S01]: Communication link failure',\n" + " 'SQLSTATE[08006] [7] could not connect to server: Connection refused Is the server running on host',\n" + " 'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: No route to host',\n" + " 'The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.',\n" + " 'SQLSTATE[08006] [7] could not translate host name',\n" + " 'TCP Provider: Error code 0x274C',\n" + " 'SQLSTATE[HY000] [2002] No such file or directory',\n" + " 'Reason: Server is in script upgrade mode. Only administrator can connect at this time.',\n" + " 'Unknown $curl_error_code: 77',\n" + " 'SQLSTATE[08006] [7] SSL error: sslv3 alert unexpected message',\n" + " 'SQLSTATE[08006] [7] unrecognized SSL error code:',\n" + " 'SQLSTATE[HY000] [2002] No connection could be made because the target machine actively refused it',\n" + " 'Broken pipe',\n" + " // PDO::prepare(): Send of 77 bytes failed with errno=110 Operation timed out\n" + " // SSL: Handshake timed out\n" + " // SSL: Operation timed out\n" + " // SSL: Connection timed out\n" + " // SQLSTATE[HY000] [2002] Connection timed out\n" + " 'timed out',\n" + " 'Error reading result',\n" + " ];\n" + "\n" + " public static function causedByLostConnection(\\Throwable $e): bool\n" + " {\n" + " $message = $e->getMessage();\n" + " foreach (self::ERROR_MESSAGES as $needle) {\n" + " if (mb_strpos($message, $needle) !== false) {\n" + " return true;\n" + " }\n" + " }\n" + "\n" + " return false;\n" + " }\n" + "}\n"; + static const char* swoole_library_source_core_database_pdo_config = "\n" "/**\n" @@ -2461,32 +2823,23 @@ static const char* swoole_library_source_core_database_pdo_config = "{\n" " public const DRIVER_MYSQL = 'mysql';\n" "\n" - " /** @var string */\n" - " protected $driver = self::DRIVER_MYSQL;\n" + " protected string $driver = self::DRIVER_MYSQL;\n" "\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" + " protected string $host = '127.0.0.1';\n" "\n" - " /** @var int */\n" - " protected $port = 3306;\n" + " protected int $port = 3306;\n" "\n" - " /** @var null|string */\n" - " protected $unixSocket;\n" + " protected ?string $unixSocket;\n" "\n" - " /** @var string */\n" - " protected $dbname = 'test';\n" + " protected string $dbname = 'test';\n" "\n" - " /** @var string */\n" - " protected $charset = 'utf8mb4';\n" + " protected string $charset = 'utf8mb4';\n" "\n" - " /** @var string */\n" - " protected $username = 'root';\n" + " protected string $username = 'root';\n" "\n" - " /** @var string */\n" - " protected $password = 'root';\n" + " protected string $password = 'root';\n" "\n" - " /** @var array */\n" - " protected $options = [];\n" + " protected array $options = [];\n" "\n" " public function getDriver(): string\n" " {\n" @@ -2504,7 +2857,7 @@ static const char* swoole_library_source_core_database_pdo_config = " return $this->host;\n" " }\n" "\n" - " public function withHost($host): self\n" + " public function withHost(string $host): self\n" " {\n" " $this->host = $host;\n" " return $this;\n" @@ -2517,12 +2870,12 @@ static const char* swoole_library_source_core_database_pdo_config = "\n" " public function hasUnixSocket(): bool\n" " {\n" - " return isset($this->unixSocket);\n" + " return !empty($this->unixSocket);\n" " }\n" "\n" - " public function getUnixSocket(): string\n" + " public function getUnixSocket(): ?string\n" " {\n" - " return $this->unixSocket;\n" + " return $this->unixSocket ?? null;\n" " }\n" "\n" " public function withUnixSocket(?string $unixSocket): self\n" @@ -2597,7 +2950,7 @@ static const char* swoole_library_source_core_database_pdo_config = " *\n" " * @return string[]\n" " */\n" - " public static function getAvailableDrivers()\n" + " public static function getAvailableDrivers(): array\n" " {\n" " return [\n" " self::DRIVER_MYSQL,\n" @@ -2623,116 +2976,145 @@ 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"; - -static const char* swoole_library_source_core_database_pdo_proxy = - "\n" - "/**\n" - " * This file is part of Swoole.\n" - " *\n" - " * @link https://www.swoole.com\n" - " * @contact team@swoole.com\n" - " * @license https://github.com/swoole/library/blob/master/LICENSE\n" - " */\n" + "\n" + " /**\n" + " * 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 = + "\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" - "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" - " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" - " $exception->errorInfo = $errorInfo;\n" - " throw $exception;\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 (\n" - " strcasecmp($name, 'prepare') === 0 ||\n" - " strcasecmp($name, 'query') === 0\n" - " ) {\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" @@ -2745,12 +3127,11 @@ static const char* swoole_library_source_core_database_pdo_proxy = " {\n" " $constructor = $this->constructor;\n" " parent::__construct($constructor());\n" + " $this->__object->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_EXCEPTION);\n" " $this->round++;\n" " /* restore context */\n" - " if ($this->setAttributeContext) {\n" - " foreach ($this->setAttributeContext as $attribute => $value) {\n" - " $this->__object->setAttribute($attribute, $value);\n" - " }\n" + " foreach ($this->setAttributeContext as $attribute => $value) {\n" + " $this->__object->setAttribute($attribute, $value);\n" " }\n" " }\n" "\n" @@ -2762,7 +3143,12 @@ static const char* swoole_library_source_core_database_pdo_proxy = "\n" " public function inTransaction(): bool\n" " {\n" - " return $this->__object->inTransaction();\n" + " return $this->inTransaction > 0;\n" + " }\n" + "\n" + " public function reset(): void\n" + " {\n" + " $this->inTransaction = 0;\n" " }\n" "}\n"; @@ -2780,107 +3166,72 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = "\n" "namespace Swoole\\Database;\n" "\n" - "use PDO;\n" - "use PDOException;\n" - "use PDOStatement;\n" - "\n" + "/**\n" + " * The proxy class for PHP class PDOStatement.\n" + " *\n" + " * @see https://www.php.net/PDOStatement The PDOStatement class\n" + " */\n" "class PDOStatementProxy extends ObjectProxy\n" "{\n" - " /** @var PDOStatement */\n" + " /** @var \\PDOStatement */\n" " protected $__object;\n" "\n" - " /** @var null|array */\n" - " protected $setAttributeContext;\n" + " protected array $setAttributeContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $setFetchModeContext;\n" + " protected array $setFetchModeContext;\n" "\n" - " /** @var null|array */\n" - " protected $bindParamContext;\n" + " protected array $bindParamContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $bindColumnContext;\n" + " protected array $bindColumnContext = [];\n" "\n" - " /** @var null|array */\n" - " protected $bindValueContext;\n" + " protected array $bindValueContext = [];\n" "\n" - " /** @var PDO|PDOProxy */\n" - " protected $parent;\n" + " protected PDOProxy $parent;\n" "\n" " /** @var int */\n" " protected $parentRound;\n" "\n" - " public function __construct(PDOStatement $object, PDOProxy $parent)\n" + " public function __construct(\\PDOStatement $object, PDOProxy $parent)\n" " {\n" " parent::__construct($object);\n" - " $this->parent = $parent;\n" + " $this->parent = $parent;\n" " $this->parentRound = $parent->getRound();\n" " }\n" "\n" " public function __call(string $name, array $arguments)\n" " {\n" - " for ($n = 3; $n--;) {\n" - " $ret = @$this->__object->{$name}(...$arguments);\n" - " if ($ret === false) {\n" - " /* no IO */\n" - " if (strtolower($name) !== 'execute') {\n" - " break;\n" - " }\n" - " /* no more chances or non-IO failures or in transaction */\n" - " if (\n" - " !in_array($this->__object->errorInfo()[1], $this->parent::IO_ERRORS, true) ||\n" - " $n === 0 ||\n" - " $this->parent->inTransaction()\n" - " ) {\n" - " $errorInfo = $this->__object->errorInfo();\n" - "\n" - " // '00000' means “no error.”, as specified by ANSI SQL and ODBC.\n" - " if ($errorInfo[0] !== '00000') {\n" - " $exception = new PDOException($errorInfo[2], $errorInfo[1]);\n" - " $exception->errorInfo = $errorInfo;\n" - " throw $exception;\n" - " }\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" @@ -2890,16 +3241,18 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " return $this->__object->setAttribute($attribute, $value);\n" " }\n" "\n" - " public function setFetchMode(int $mode, $classNameObject = null, array $ctorarfg = []): 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 = [$mode, $classNameObject, $ctorarfg];\n" - " if (!isset($classNameObject)) {\n" - " return $this->__object->setFetchMode($mode);\n" - " }\n" - " return $this->__object->setFetchMode($mode, $classNameObject, $ctorarfg);\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" @@ -2911,7 +3264,7 @@ static const char* swoole_library_source_core_database_pdo_statement_proxy = " return $this->__object->bindColumn($column, $param, $type, $maxlen, $driverdata);\n" " }\n" "\n" - " public function bindValue($parameter, $value, $data_type = PDO::PARAM_STR): bool\n" + " public function bindValue($parameter, $value, $data_type = \\PDO::PARAM_STR): bool\n" " {\n" " $this->bindValueContext[$parameter] = [$value, $data_type];\n" " return $this->__object->bindValue($parameter, $value, $data_type);\n" @@ -2934,36 +3287,33 @@ static const char* swoole_library_source_core_database_redis_config = "\n" "class RedisConfig\n" "{\n" - " /** @var string */\n" - " protected $host = '127.0.0.1';\n" + " protected string $host = '127.0.0.1';\n" "\n" - " /** @var int */\n" - " protected $port = 6379;\n" + " protected int $port = 6379;\n" "\n" - " /** @var float */\n" - " protected $timeout = 0.0;\n" + " protected float $timeout = 0.0;\n" "\n" - " /** @var string */\n" - " protected $reserved = '';\n" + " protected string $reserved = '';\n" "\n" - " /** @var int */\n" - " protected $retry_interval = 0;\n" + " protected int $retry_interval = 0;\n" "\n" - " /** @var float */\n" - " protected $read_timeout = 0.0;\n" + " protected float $read_timeout = 0.0;\n" "\n" - " /** @var string */\n" - " protected $auth = '';\n" + " protected string $auth = '';\n" "\n" - " /** @var int */\n" - " protected $dbIndex = 0;\n" + " protected int $dbIndex = 0;\n" + "\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $options = [];\n" "\n" - " public function getHost()\n" + " public function getHost(): string\n" " {\n" " return $this->host;\n" " }\n" "\n" - " public function withHost($host): self\n" + " public function withHost(string $host): self\n" " {\n" " $this->host = $host;\n" " return $this;\n" @@ -3045,6 +3395,36 @@ static const char* swoole_library_source_core_database_redis_config = " $this->dbIndex = $dbIndex;\n" " return $this;\n" " }\n" + "\n" + " /**\n" + " * Add a configurable option.\n" + " */\n" + " public function withOption(int $option, mixed $value): self\n" + " {\n" + " $this->options[$option] = $value;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * Add/override configurable options.\n" + " *\n" + " * @param array $options\n" + " */\n" + " public function setOptions(array $options): self\n" + " {\n" + " $this->options = $options;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * Get configurable options.\n" + " *\n" + " * @return array\n" + " */\n" + " public function getOptions(): array\n" + " {\n" + " return $this->options;\n" + " }\n" "}\n"; static const char* swoole_library_source_core_database_redis_pool = @@ -3065,19 +3445,15 @@ static const char* swoole_library_source_core_database_redis_pool = "use Swoole\\ConnectionPool;\n" "\n" "/**\n" - " * @method Redis get()\n" + " * @method \\Redis get()\n" " * @method void put(Redis $connection)\n" " */\n" "class RedisPool extends ConnectionPool\n" "{\n" - " /** @var RedisConfig */\n" - " protected $config;\n" - "\n" - " public function __construct(RedisConfig $config, int $size = self::DEFAULT_SIZE)\n" + " public function __construct(protected RedisConfig $config, int $size = self::DEFAULT_SIZE)\n" " {\n" - " $this->config = $config;\n" " parent::__construct(function () {\n" - " $redis = new Redis();\n" + " $redis = new \\Redis();\n" " /* Compatible with different versions of Redis extension as much as possible */\n" " $arguments = [\n" " $this->config->getHost(),\n" @@ -3101,6 +3477,12 @@ static const char* swoole_library_source_core_database_redis_pool = " if ($this->config->getDbIndex() !== 0) {\n" " $redis->select($this->config->getDbIndex());\n" " }\n" + "\n" + " /* Set Redis options. */\n" + " foreach ($this->config->getOptions() as $key => $value) {\n" + " $redis->setOption($key, $value);\n" + " }\n" + "\n" " return $redis;\n" " }, $size);\n" " }\n" @@ -3245,66 +3627,66 @@ static const char* swoole_library_source_core_http_status = " public const NETWORK_AUTHENTICATION_REQUIRED = 511;\n" "\n" " protected static $reasonPhrases = [\n" - " self::CONTINUE => 'Continue',\n" - " self::SWITCHING_PROTOCOLS => 'Switching Protocols',\n" - " self::PROCESSING => 'Processing',\n" - " self::OK => 'OK',\n" - " self::CREATED => 'Created',\n" - " self::ACCEPTED => 'Accepted',\n" - " self::NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information',\n" - " self::NO_CONTENT => 'No Content',\n" - " self::RESET_CONTENT => 'Reset Content',\n" - " self::PARTIAL_CONTENT => 'Partial Content',\n" - " self::MULTI_STATUS => 'Multi-status',\n" - " self::ALREADY_REPORTED => 'Already Reported',\n" - " self::IM_USED => 'IM Used',\n" - " self::MULTIPLE_CHOICES => 'Multiple Choices',\n" - " self::MOVED_PERMANENTLY => 'Moved Permanently',\n" - " self::FOUND => 'Found',\n" - " self::SEE_OTHER => 'See Other',\n" - " self::NOT_MODIFIED => 'Not Modified',\n" - " self::USE_PROXY => 'Use Proxy',\n" - " self::SWITCH_PROXY => 'Switch Proxy',\n" - " self::TEMPORARY_REDIRECT => 'Temporary Redirect',\n" - " self::PERMANENT_REDIRECT => 'Permanent Redirect',\n" - " self::BAD_REQUEST => 'Bad Request',\n" - " self::UNAUTHORIZED => 'Unauthorized',\n" - " self::PAYMENT_REQUIRED => 'Payment Required',\n" - " self::FORBIDDEN => 'Forbidden',\n" - " self::NOT_FOUND => 'Not Found',\n" - " self::METHOD_NOT_ALLOWED => 'Method Not Allowed',\n" - " self::NOT_ACCEPTABLE => 'Not Acceptable',\n" - " self::PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required',\n" - " self::REQUEST_TIME_OUT => 'Request Time-out',\n" - " self::CONFLICT => 'Conflict',\n" - " self::GONE => 'Gone',\n" - " self::LENGTH_REQUIRED => 'Length Required',\n" - " self::PRECONDITION_FAILED => 'Precondition Failed',\n" - " self::REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large',\n" - " self::REQUEST_URI_TOO_LARGE => 'Request-URI Too Large',\n" - " self::UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',\n" + " self::CONTINUE => 'Continue',\n" + " self::SWITCHING_PROTOCOLS => 'Switching Protocols',\n" + " self::PROCESSING => 'Processing',\n" + " self::OK => 'OK',\n" + " self::CREATED => 'Created',\n" + " self::ACCEPTED => 'Accepted',\n" + " self::NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information',\n" + " self::NO_CONTENT => 'No Content',\n" + " self::RESET_CONTENT => 'Reset Content',\n" + " self::PARTIAL_CONTENT => 'Partial Content',\n" + " self::MULTI_STATUS => 'Multi-status',\n" + " self::ALREADY_REPORTED => 'Already Reported',\n" + " self::IM_USED => 'IM Used',\n" + " self::MULTIPLE_CHOICES => 'Multiple Choices',\n" + " self::MOVED_PERMANENTLY => 'Moved Permanently',\n" + " self::FOUND => 'Found',\n" + " self::SEE_OTHER => 'See Other',\n" + " self::NOT_MODIFIED => 'Not Modified',\n" + " self::USE_PROXY => 'Use Proxy',\n" + " self::SWITCH_PROXY => 'Switch Proxy',\n" + " self::TEMPORARY_REDIRECT => 'Temporary Redirect',\n" + " self::PERMANENT_REDIRECT => 'Permanent Redirect',\n" + " self::BAD_REQUEST => 'Bad Request',\n" + " self::UNAUTHORIZED => 'Unauthorized',\n" + " self::PAYMENT_REQUIRED => 'Payment Required',\n" + " self::FORBIDDEN => 'Forbidden',\n" + " self::NOT_FOUND => 'Not Found',\n" + " self::METHOD_NOT_ALLOWED => 'Method Not Allowed',\n" + " self::NOT_ACCEPTABLE => 'Not Acceptable',\n" + " self::PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required',\n" + " self::REQUEST_TIME_OUT => 'Request Time-out',\n" + " self::CONFLICT => 'Conflict',\n" + " self::GONE => 'Gone',\n" + " self::LENGTH_REQUIRED => 'Length Required',\n" + " self::PRECONDITION_FAILED => 'Precondition Failed',\n" + " self::REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large',\n" + " self::REQUEST_URI_TOO_LARGE => 'Request-URI Too Large',\n" + " self::UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',\n" " self::REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested range not satisfiable',\n" - " self::EXPECTATION_FAILED => 'Expectation Failed',\n" - " self::MISDIRECTED_REQUEST => 'Unprocessable Entity',\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" @@ -3355,52 +3737,51 @@ static const char* swoole_library_source_core_curl_handler = "\n" "namespace Swoole\\Curl;\n" "\n" - "use CURLFile;\n" - "use ReflectionClass;\n" - "use Swoole;\n" "use Swoole\\Constant;\n" "use Swoole\\Coroutine\\Http\\Client;\n" + "use Swoole\\Coroutine\\System;\n" "use Swoole\\Curl\\Exception as CurlException;\n" "use Swoole\\Http\\Status;\n" "\n" - "final class Handler\n" + "final class Handler implements \\Stringable\n" "{\n" " /**\n" - " * @var Client\n" + " * @var Client|null\n" " */\n" " private $client;\n" "\n" " private $info = [\n" - " 'url' => '',\n" - " 'content_type' => '',\n" - " 'http_code' => 0,\n" - " 'header_size' => 0,\n" - " 'request_size' => 0,\n" - " 'filetime' => -1,\n" - " 'ssl_verify_result' => 0,\n" - " 'redirect_count' => 0,\n" - " 'total_time' => 5.3E-5,\n" - " 'namelookup_time' => 0.0,\n" - " 'connect_time' => 0.0,\n" - " 'pretransfer_time' => 0.0,\n" - " 'size_upload' => 0.0,\n" - " 'size_download' => 0.0,\n" - " 'speed_download' => 0.0,\n" - " 'speed_upload' => 0.0,\n" + " 'url' => '',\n" + " 'content_type' => '',\n" + " 'http_code' => 0,\n" + " 'header_size' => 0,\n" + " 'request_size' => 0,\n" + " 'filetime' => -1,\n" + " 'ssl_verify_result' => 0,\n" + " 'redirect_count' => 0,\n" + " 'total_time' => 5.3E-5,\n" + " 'namelookup_time' => 0.0,\n" + " 'connect_time' => 0.0,\n" + " 'pretransfer_time' => 0.0,\n" + " 'size_upload' => 0.0,\n" + " 'size_download' => 0.0,\n" + " 'speed_download' => 0.0,\n" + " 'speed_upload' => 0.0,\n" " 'download_content_length' => -1.0,\n" - " 'upload_content_length' => -1.0,\n" - " 'starttransfer_time' => 0.0,\n" - " 'redirect_time' => 0.0,\n" - " 'redirect_url' => '',\n" - " 'primary_ip' => '',\n" - " 'certinfo' => [],\n" - " 'primary_port' => 0,\n" - " 'local_ip' => '',\n" - " 'local_port' => 0,\n" - " 'http_version' => 0,\n" - " 'protocol' => 0,\n" - " 'ssl_verifyresult' => 0,\n" - " 'scheme' => '',\n" + " '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" @@ -3439,15 +3820,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" @@ -3471,6 +3854,10 @@ static const char* swoole_library_source_core_curl_handler = "\n" " private $cookieJar = '';\n" "\n" + " private $resolve = [];\n" + "\n" + " private $unix_socket_path = '';\n" + "\n" " public function __construct(string $url = '')\n" " {\n" " if ($url) {\n" @@ -3478,7 +3865,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " }\n" "\n" - " public function __toString()\n" + " public function __toString(): string\n" " {\n" " $id = spl_object_id($this);\n" " return \"Object({$id}) of type (curl)\";\n" @@ -3513,14 +3900,14 @@ static const char* swoole_library_source_core_curl_handler = " return $this->isAvailable() ? $this->info : false;\n" " }\n" "\n" - " public function errno()\n" + " public function errno(): int\n" " {\n" - " return $this->isAvailable() ? $this->errCode : false;\n" + " return $this->isAvailable() ? $this->errCode : 0;\n" " }\n" "\n" - " public function error()\n" + " public function error(): string\n" " {\n" - " return $this->isAvailable() ? $this->errMsg : false;\n" + " return $this->isAvailable() ? $this->errMsg : '';\n" " }\n" "\n" " public function reset()\n" @@ -3528,7 +3915,7 @@ static const char* swoole_library_source_core_curl_handler = " if (!$this->isAvailable()) {\n" " return false;\n" " }\n" - " foreach ((new ReflectionClass(static::class))->getDefaultProperties() as $name => $value) {\n" + " foreach ((new \\ReflectionClass(self::class))->getDefaultProperties() as $name => $value) {\n" " $this->{$name} = $value;\n" " }\n" " }\n" @@ -3541,12 +3928,12 @@ static const char* swoole_library_source_core_curl_handler = " return $this->transfer;\n" " }\n" "\n" - " public function close()\n" + " public function close(): void\n" " {\n" " if (!$this->isAvailable()) {\n" - " return false;\n" + " return;\n" " }\n" - " foreach ($this as &$property) {\n" + " foreach ($this as &$property) { // @phpstan-ignore foreach.nonIterable\n" " $property = null;\n" " }\n" " $this->closed = true;\n" @@ -3557,7 +3944,22 @@ static const char* swoole_library_source_core_curl_handler = " if ($urlInfo === null) {\n" " $urlInfo = $this->urlInfo;\n" " }\n" - " $this->client = new Client($urlInfo['host'], $urlInfo['port'], $urlInfo['scheme'] === 'https');\n" + " $host = $urlInfo['host'];\n" + " $port = $urlInfo['port'];\n" + " if (isset($this->resolve[$host])) {\n" + " if (!$this->hasHeader('Host')) {\n" + " $this->setHeader('Host', $host);\n" + " }\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" " private function getUrl(): string\n" @@ -3582,11 +3984,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" @@ -3619,13 +4029,9 @@ 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 (\n" - " $host !== $oldUrlInfo['host'] or\n" - " $port !== $oldUrlInfo['port'] or\n" - " $scheme !== $oldUrlInfo['scheme']\n" - " ) {\n" + " if (($host !== $oldUrlInfo['host']) || ($port !== $oldUrlInfo['port']) || ($scheme !== $oldUrlInfo['scheme'])) {\n" " /* target changed */\n" " $this->create($urlInfo);\n" " }\n" @@ -3637,9 +4043,9 @@ static const char* swoole_library_source_core_curl_handler = " private function setPort(int $port): void\n" " {\n" " $this->info['primary_port'] = $port;\n" - " if ($this->urlInfo['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" @@ -3649,7 +4055,7 @@ static const char* swoole_library_source_core_curl_handler = " private function setError($code, $msg = ''): void\n" " {\n" " $this->errCode = $code;\n" - " $this->errMsg = $msg ? $msg : curl_strerror($code);\n" + " $this->errMsg = $msg ?: curl_strerror($code);\n" " }\n" "\n" " private function hasHeader(string $headerName): bool\n" @@ -3666,7 +4072,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($value !== '') {\n" - " $this->headers[$headerName] = $value;\n" + " $this->headers[$headerName] = $value;\n" " $this->headerMap[$lowerCaseHeaderName] = $headerName;\n" " } else {\n" " // remove empty headers (keep same with raw cURL)\n" @@ -3675,10 +4081,9 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " /**\n" - " * @param mixed $value\n" - " * @throws Swoole\\Curl\\Exception\n" + " * @throws Exception\n" " */\n" - " private function setOption(int $opt, $value): bool\n" + " private function setOption(int $opt, mixed $value): bool\n" " {\n" " switch ($opt) {\n" " // case CURLOPT_STDERR:\n" @@ -3686,7 +4091,7 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_FILE:\n" " case CURLOPT_INFILE:\n" " if (!is_resource($value)) {\n" - " trigger_error(E_USER_WARNING, 'swoole_curl_setopt(): supplied argument is not a valid File-Handle resource');\n" + " trigger_error('swoole_curl_setopt(): supplied argument is not a valid File-Handle resource', E_USER_WARNING);\n" " return false;\n" " }\n" " break;\n" @@ -3705,8 +4110,8 @@ static const char* swoole_library_source_core_curl_handler = " $this->clientOptions[Constant::OPTION_KEEP_ALIVE] = !$value;\n" " break;\n" " case CURLOPT_RETURNTRANSFER:\n" - " $this->returnTransfer = $value;\n" - " $this->transfer = '';\n" + " $this->returnTransfer = (bool) $value;\n" + " $this->transfer = '';\n" " break;\n" " case CURLOPT_ENCODING:\n" " if (empty($value)) {\n" @@ -3728,9 +4133,7 @@ static const char* swoole_library_source_core_curl_handler = " break;\n" " case CURLOPT_PROXYTYPE:\n" " if ($value !== CURLPROXY_HTTP and $value !== CURLPROXY_SOCKS5) {\n" - " throw new Swoole\\Curl\\Exception(\n" - " 'swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5'\n" - " );\n" + " throw new CurlException('swoole_curl_setopt(): Only support following CURLOPT_PROXYTYPE values: CURLPROXY_HTTP, CURLPROXY_SOCKS5');\n" " }\n" " $this->proxyType = $value;\n" " break;\n" @@ -3747,39 +4150,63 @@ 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" " break;\n" + " case CURLOPT_RESOLVE:\n" + " foreach ((array) $value as $resolve) {\n" + " $flag = substr($resolve, 0, 1);\n" + " if ($flag === '+' || $flag === '-') {\n" + " // TODO: [+]HOST:PORT:ADDRESS\n" + " $resolve = substr($resolve, 1);\n" + " }\n" + " $tmpResolve = explode(':', $resolve, 3);\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" + " // TODO: HOST:PORT:ADDRESS[,ADDRESS]...\n" + " $this->resolve[$host][$port] = explode(',', $ip)[0];\n" + " }\n" + " }\n" + " 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" " $this->clientOptions[Constant::OPTION_OPEN_TCP_NODELAY] = boolval($value);\n" " break;\n" - " /*\n" - " * Ignore options\n" - " */\n" + " case CURLOPT_PRIVATE:\n" + " $this->info['private'] = $value;\n" + " break;\n" + " /*\n" + " * Ignore options\n" + " */\n" " case CURLOPT_VERBOSE:\n" - " // trigger_error(E_USER_WARNING, 'swoole_curl_setopt(): CURLOPT_VERBOSE is not supported');\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" @@ -3791,10 +4218,11 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_CERTINFO:\n" " case CURLOPT_HEADEROPT:\n" " case CURLOPT_PROXYHEADER:\n" + " case CURLOPT_HTTPPROXYTUNNEL:\n" " break;\n" - " /*\n" - " * SSL\n" - " */\n" + " /*\n" + " * SSL\n" + " */\n" " case CURLOPT_SSL_VERIFYHOST:\n" " break;\n" " case CURLOPT_SSL_VERIFYPEER:\n" @@ -3817,9 +4245,9 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_SSLKEYPASSWD:\n" " $this->clientOptions[Constant::OPTION_SSL_PASSPHRASE] = $value;\n" " break;\n" - " /*\n" - " * Http POST\n" - " */\n" + " /*\n" + " * Http POST\n" + " */\n" " case CURLOPT_POST:\n" " $this->method = 'POST';\n" " break;\n" @@ -3829,26 +4257,26 @@ static const char* swoole_library_source_core_curl_handler = " $this->method = 'POST';\n" " }\n" " break;\n" - " /*\n" - " * Upload\n" - " */\n" + " /*\n" + " * Upload\n" + " */\n" " case CURLOPT_SAFE_UPLOAD:\n" " if (!$value) {\n" " trigger_error('swoole_curl_setopt(): Disabling safe uploads is no longer supported', E_USER_WARNING);\n" " return false;\n" " }\n" " break;\n" - " /*\n" - " * Http Header\n" - " */\n" + " /*\n" + " * Http Header\n" + " */\n" " case CURLOPT_HTTPHEADER:\n" " if (!is_array($value) and !is_iterable($value)) {\n" " trigger_error('swoole_curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument', E_USER_WARNING);\n" " return false;\n" " }\n" " foreach ($value as $header) {\n" - " $header = explode(':', $header, 2);\n" - " $headerName = $header[0];\n" + " $header = explode(':', $header, 2);\n" + " $headerName = $header[0];\n" " $headerValue = trim($header[1] ?? '');\n" " $this->setHeader($headerName, $headerValue);\n" " }\n" @@ -3887,9 +4315,9 @@ static const char* swoole_library_source_core_curl_handler = " case CURLOPT_FAILONERROR:\n" " $this->failOnError = $value;\n" " break;\n" - " /*\n" - " * Http Cookie\n" - " */\n" + " /*\n" + " * Http Cookie\n" + " */\n" " case CURLOPT_COOKIE:\n" " $this->setHeader('Cookie', $value);\n" " break;\n" @@ -3928,6 +4356,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" @@ -3965,7 +4396,7 @@ static const char* swoole_library_source_core_curl_handler = " $this->method = 'GET';\n" " break;\n" " default:\n" - " throw new Swoole\\Curl\\Exception(\"swoole_curl_setopt(): option[{$opt}] is not supported\");\n" + " throw new CurlException(\"swoole_curl_setopt(): option[{$opt}] is not supported\");\n" " }\n" " return true;\n" " }\n" @@ -3973,8 +4404,8 @@ static const char* swoole_library_source_core_curl_handler = " private function execute()\n" " {\n" " $this->info['redirect_count'] = $this->info['starttransfer_time'] = 0;\n" - " $this->info['redirect_url'] = '';\n" - " $timeBegin = microtime(true);\n" + " $this->info['redirect_url'] = '';\n" + " $timeBegin = microtime(true);\n" " /*\n" " * Socket\n" " */\n" @@ -3982,46 +4413,52 @@ 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" - " $proxy = explode(':', $this->proxy);\n" - " $proxyPort = $proxy[1] ?? $this->proxyPort;\n" - " $proxy = $proxy[0];\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" + " if (is_string($proxyType)) {\n" + " if ($proxyType === 'socks5') {\n" + " $proxyType = CURLPROXY_SOCKS5;\n" + " } else {\n" + " $proxyType = CURLPROXY_HTTP;\n" + " }\n" + " }\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 ($this->proxyType) {\n" - " case CURLPROXY_HTTP:\n" - " $proxyOptions = [\n" - " 'http_proxy_host' => $proxy,\n" - " 'http_proxy_port' => $proxyPort,\n" - " 'http_proxy_username' => $this->proxyUsername,\n" - " 'http_proxy_password' => $this->proxyPassword,\n" - " ];\n" - " break;\n" - " case CURLPROXY_SOCKS5:\n" - " $proxyOptions = [\n" - " 'socks5_host' => $proxy,\n" - " 'socks5_port' => $proxyPort,\n" - " 'socks5_username' => $this->proxyUsername,\n" - " 'socks5_password' => $this->proxyPassword,\n" - " ];\n" - " break;\n" - " default:\n" - " throw new CurlException(\"Unexpected proxy type [{$this->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" @@ -4055,7 +4492,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " $client->setData($data);\n" " // Notice: although we reset it, raw cURL never do this\n" - " $this->infile = null;\n" + " $this->infile = null;\n" " $this->infileSize = PHP_INT_MAX;\n" " } else {\n" " // POST data\n" @@ -4066,14 +4503,14 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " } elseif (is_array($this->postData)) {\n" " foreach ($this->postData as $k => $v) {\n" - " if ($v instanceof CURLFile) {\n" + " if ($v instanceof \\CURLFile) {\n" " $client->addFile($v->getFilename(), $k, $v->getMimeType() ?: 'application/octet-stream', $v->getPostFilename());\n" " unset($this->postData[$k]);\n" " }\n" " }\n" " }\n" + " $client->setData($this->postData);\n" " }\n" - " $client->setData($this->postData);\n" " }\n" " /*\n" " * Headers\n" @@ -4098,11 +4535,11 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " if ($client->statusCode >= 300 and $client->statusCode < 400 and isset($client->headers['location'])) {\n" " $redirectParsedUrl = $this->getRedirectUrl($client->headers['location']);\n" - " $redirectUrl = static::unparseUrl($redirectParsedUrl);\n" + " $redirectUrl = self::unparseUrl($redirectParsedUrl);\n" " if ($this->followLocation and ($this->maxRedirects === null or $this->info['redirect_count'] < $this->maxRedirects)) {\n" " if ($this->info['redirect_count'] === 0) {\n" " $this->info['starttransfer_time'] = microtime(true) - $timeBegin;\n" - " $redirectBeginTime = microtime(true);\n" + " $redirectBeginTime = microtime(true);\n" " }\n" " // force GET\n" " if (in_array($client->statusCode, [Status::MOVED_PERMANENTLY, Status::FOUND, Status::SEE_OTHER])) {\n" @@ -4124,16 +4561,25 @@ 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" " }\n" "\n" + " if (filter_var($this->urlInfo['host'], FILTER_VALIDATE_IP)) {\n" + " $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" @@ -4172,7 +4618,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($this->withHeaderOut) {\n" - " $headerOutContent = $client->getHeaderOut();\n" + " $headerOutContent = $client->getHeaderOut();\n" " $this->info['request_header'] = $headerOutContent ? $headerOutContent . \"\\r\\n\\r\\n\" : '';\n" " }\n" " if ($this->withFileTime) {\n" @@ -4183,7 +4629,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" " }\n" "\n" - " if ($this->cookieJar && $this->cookieJar !== '') {\n" + " if (!empty($this->cookieJar)) {\n" " if ($this->cookieJar === '-') {\n" " foreach ((array) $client->set_cookie_headers as $cookie) {\n" " echo $cookie . PHP_EOL;\n" @@ -4198,7 +4644,7 @@ static const char* swoole_library_source_core_curl_handler = " }\n" "\n" " if ($this->writeFunction) {\n" - " if (!is_callable($this->writeFunction)) {\n" + " if (!is_callable($this->writeFunction)) { // @phpstan-ignore booleanNot.alwaysFalse\n" " trigger_error('curl_exec(): Could not call the CURLOPT_WRITEFUNCTION', E_USER_WARNING);\n" " $this->setError(CURLE_WRITE_ERROR, 'Failure writing output to destination');\n" " return false;\n" @@ -4222,14 +4668,14 @@ static const char* swoole_library_source_core_curl_handler = "\n" " private static function unparseUrl(array $parsedUrl): string\n" " {\n" - " $scheme = ($parsedUrl['scheme'] ?? 'http') . '://';\n" - " $host = $parsedUrl['host'] ?? '';\n" - " $port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';\n" - " $user = $parsedUrl['user'] ?? '';\n" - " $pass = isset($parsedUrl['pass']) ? ':' . $parsedUrl['pass'] : '';\n" - " $pass = ($user or $pass) ? \"{$pass}@\" : '';\n" - " $path = $parsedUrl['path'] ?? '';\n" - " $query = (isset($parsedUrl['query']) and $parsedUrl['query'] !== '') ? '?' . $parsedUrl['query'] : '';\n" + " $scheme = ($parsedUrl['scheme'] ?? 'http') . '://';\n" + " $host = $parsedUrl['host'] ?? '';\n" + " $port = isset($parsedUrl['port']) ? ':' . $parsedUrl['port'] : '';\n" + " $user = $parsedUrl['user'] ?? '';\n" + " $pass = isset($parsedUrl['pass']) ? ':' . $parsedUrl['pass'] : '';\n" + " $pass = ($user or $pass) ? \"{$pass}@\" : '';\n" + " $path = $parsedUrl['path'] ?? '';\n" + " $query = (isset($parsedUrl['query']) and $parsedUrl['query'] !== '') ? '?' . $parsedUrl['query'] : '';\n" " $fragment = isset($parsedUrl['fragment']) ? '#' . $parsedUrl['fragment'] : '';\n" " return $scheme . $user . $pass . $host . $port . $path . $query . $fragment;\n" " }\n" @@ -4243,7 +4689,7 @@ static const char* swoole_library_source_core_curl_handler = " if (!isset($location[0])) {\n" " return [];\n" " }\n" - " $redirectUri = $this->urlInfo;\n" + " $redirectUri = $this->urlInfo;\n" " $redirectUri['query'] = '';\n" " if ($location[0] === '/') {\n" " $redirectUri['path'] = $location;\n" @@ -4252,14 +4698,16 @@ 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" " }\n" - " foreach ($uri as $k => $v) {\n" - " if (!in_array($k, ['path', 'query'])) {\n" - " $redirectUri[$k] = $v;\n" + " if (is_array($uri)) {\n" + " foreach ($uri as $k => $v) {\n" + " if (!in_array($k, ['path', 'query'])) {\n" + " $redirectUri[$k] = $v;\n" + " }\n" " }\n" " }\n" " }\n" @@ -4382,63 +4830,47 @@ static const char* swoole_library_source_core_fast_cgi_record = "/**\n" " * FastCGI record.\n" " */\n" - "class Record\n" + "class Record implements \\Stringable\n" "{\n" " /**\n" " * Identifies the FastCGI protocol version.\n" - " *\n" - " * @var int\n" " */\n" - " protected $version = FastCGI::VERSION_1;\n" + " protected int $version = FastCGI::VERSION_1;\n" "\n" " /**\n" " * Identifies the FastCGI record type, i.e. the general function that the record performs.\n" - " *\n" - " * @var int\n" " */\n" - " protected $type = FastCGI::UNKNOWN_TYPE;\n" + " protected int $type = FastCGI::UNKNOWN_TYPE;\n" "\n" " /**\n" " * Identifies the FastCGI request to which the record belongs.\n" - " *\n" - " * @var int\n" " */\n" - " protected $requestId = FastCGI::DEFAULT_REQUEST_ID;\n" + " protected int $requestId = FastCGI::DEFAULT_REQUEST_ID;\n" "\n" " /**\n" " * Reserved byte for future proposes\n" - " *\n" - " * @var int\n" " */\n" - " protected $reserved = 0;\n" + " protected int $reserved = 0;\n" "\n" " /**\n" " * The number of bytes in the contentData component of the record.\n" - " *\n" - " * @var int\n" " */\n" - " private $contentLength = 0;\n" + " private int $contentLength = 0;\n" "\n" " /**\n" " * The number of bytes in the paddingData component of the record.\n" - " *\n" - " * @var int\n" " */\n" - " private $paddingLength = 0;\n" + " private int $paddingLength = 0;\n" "\n" " /**\n" " * Binary data, between 0 and 65535 bytes of data, interpreted according to the record type.\n" - " *\n" - " * @var string\n" " */\n" - " private $contentData = '';\n" + " private string $contentData = '';\n" "\n" " /**\n" " * Padding data, between 0 and 255 bytes of data, which are ignored.\n" - " *\n" - " * @var string\n" " */\n" - " private $paddingData = '';\n" + " private string $paddingData = '';\n" "\n" " /**\n" " * Returns the binary message representation of record\n" @@ -4463,26 +4895,29 @@ static const char* swoole_library_source_core_fast_cgi_record = "\n" " /**\n" " * Unpacks the message from the binary data buffer\n" - " *\n" - " * @param string $data Binary buffer with raw data\n" - " *\n" - " * @return static\n" " */\n" - " final public static function unpack(string $data): self\n" + " final public static function unpack(string $binaryData): static\n" " {\n" - " $self = new static();\n" + " /** @var static $self */\n" + " $self = (new \\ReflectionClass(static::class))->newInstanceWithoutConstructor();\n" + "\n" + " /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int, reserved: int} */\n" + " $packet = unpack(FastCGI::HEADER_FORMAT, $binaryData);\n" + " if ($packet === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->version,\n" " $self->type,\n" " $self->requestId,\n" " $self->contentLength,\n" " $self->paddingLength,\n" - " $self->reserved\n" - " ] = array_values(unpack(FastCGI::HEADER_FORMAT, $data));\n" + " $self->reserved,\n" + " ] = array_values($packet);\n" "\n" - " $payload = substr($data, FastCGI::HEADER_LEN);\n" + " $payload = substr($binaryData, FastCGI::HEADER_LEN);\n" " self::unpackPayload($self, $payload);\n" - " if (get_called_class() !== __CLASS__ && $self->contentLength > 0) {\n" + " if (static::class !== self::class && $self->contentLength > 0) {\n" " static::unpackPayload($self, $payload);\n" " }\n" "\n" @@ -4499,11 +4934,11 @@ static const char* swoole_library_source_core_fast_cgi_record = " $this->contentLength = strlen($data);\n" " if ($this->contentLength > FastCGI::MAX_CONTENT_LENGTH) {\n" " $this->contentLength = FastCGI::MAX_CONTENT_LENGTH;\n" - " $this->contentData = substr($data, 0, FastCGI::MAX_CONTENT_LENGTH);\n" + " $this->contentData = substr($data, 0, FastCGI::MAX_CONTENT_LENGTH);\n" " } else {\n" " $this->contentData = $data;\n" " }\n" - " $extraLength = $this->contentLength % 8;\n" + " $extraLength = $this->contentLength % 8;\n" " $this->paddingLength = $extraLength ? (8 - $extraLength) : 0;\n" " return $this;\n" " }\n" @@ -4574,18 +5009,18 @@ static const char* swoole_library_source_core_fast_cgi_record = " * Method to unpack the payload for the record.\n" " *\n" " * NB: Default implementation will be always called\n" - " *\n" - " * @param static $self Instance of current frame\n" - " * @param string $data Binary data\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload(self $self, string $binaryData): void\n" " {\n" + " /** @phpstan-var false|array{contentData: string, paddingData: string} */\n" + " $payload = unpack(\"a{$self->contentLength}contentData/a{$self->paddingLength}paddingData\", $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->contentData,\n" - " $self->paddingData\n" - " ] = array_values(\n" - " unpack(\"a{$self->contentLength}contentData/a{$self->paddingLength}paddingData\", $data)\n" - " );\n" + " $self->paddingData,\n" + " ] = array_values($payload);\n" " }\n" "\n" " /**\n" @@ -4622,22 +5057,27 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " /**\n" " * List of params\n" " *\n" - " * @var array\n" + " * @var string[]\n" + " * @phpstan-var array\n" " */\n" - " protected $values = [];\n" + " protected array $values = [];\n" "\n" " /**\n" " * Constructs a param request\n" + " *\n" + " * @phpstan-param array $values\n" " */\n" - " public function __construct(array $values = [])\n" + " public function __construct(array $values)\n" " {\n" - " $this->type = FastCGI::PARAMS;\n" + " $this->type = FastCGI::PARAMS;\n" " $this->values = $values;\n" " $this->setContentData($this->packPayload());\n" " }\n" "\n" " /**\n" " * Returns an associative list of parameters\n" + " *\n" + " * @phpstan-return array\n" " */\n" " public function getValues(): array\n" " {\n" @@ -4648,55 +5088,77 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" " $currentOffset = 0;\n" " do {\n" - " [$nameLengthHigh] = array_values(unpack('CnameLengthHigh', $data));\n" - " $isLongName = ($nameLengthHigh >> 7 == 1);\n" - " $valueOffset = $isLongName ? 4 : 1;\n" - "\n" - " [$valueLengthHigh] = array_values(unpack('CvalueLengthHigh', substr($data, $valueOffset)));\n" - " $isLongValue = ($valueLengthHigh >> 7 == 1);\n" - " $dataOffset = $valueOffset + ($isLongValue ? 4 : 1);\n" + " /** @phpstan-var false|array{nameLengthHigh: int} */\n" + " $payload = unpack('CnameLengthHigh', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$nameLengthHigh] = array_values($payload);\n" + " $isLongName = ($nameLengthHigh >> 7 == 1);\n" + " $valueOffset = $isLongName ? 4 : 1;\n" + "\n" + " /** @phpstan-var false|array{valueLengthHigh: int} */\n" + " $payload = unpack('CvalueLengthHigh', substr($binaryData, $valueOffset));\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$valueLengthHigh] = array_values($payload);\n" + " $isLongValue = ($valueLengthHigh >> 7 == 1);\n" + " $dataOffset = $valueOffset + ($isLongValue ? 4 : 1);\n" "\n" " $formatParts = [\n" " $isLongName ? 'NnameLength' : 'CnameLength',\n" " $isLongValue ? 'NvalueLength' : 'CvalueLength',\n" " ];\n" - " $format = join('/', $formatParts);\n" - " [$nameLength, $valueLength] = array_values(unpack($format, $data));\n" + " $format = join('/', $formatParts);\n" + "\n" + " /** @phpstan-var false|array{nameLength: int, valueLength: int} */\n" + " $payload = unpack($format, $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$nameLength, $valueLength] = array_values($payload);\n" "\n" " // Clear top bit for long record\n" - " $nameLength &= ($isLongName ? 0x7fffffff : 0x7f);\n" - " $valueLength &= ($isLongValue ? 0x7fffffff : 0x7f);\n" - "\n" - " [$nameData, $valueData] = array_values(\n" - " unpack(\n" - " \"a{$nameLength}nameData/a{$valueLength}valueData\",\n" - " substr($data, $dataOffset)\n" - " )\n" + " $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" @@ -4704,6 +5166,7 @@ static const char* swoole_library_source_core_fast_cgi_record_params = " \"a{$nameLength}\",\n" " \"a{$valueLength}\",\n" " ];\n" + "\n" " $format = join('', $formatParts);\n" "\n" " $payload .= pack(\n" @@ -4741,7 +5204,7 @@ static const char* swoole_library_source_core_fast_cgi_record_abort_request = " */\n" "class AbortRequest extends Record\n" "{\n" - " public function __construct(int $requestId = 0)\n" + " public function __construct(int $requestId)\n" " {\n" " $this->type = FastCGI::ABORT_REQUEST;\n" " $this->setRequestId($requestId);\n" @@ -4776,10 +5239,8 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * FCGI_RESPONDER\n" " * FCGI_AUTHORIZER\n" " * FCGI_FILTER\n" - " *\n" - " * @var int\n" " */\n" - " protected $role = FastCGI::UNKNOWN_ROLE;\n" + " protected int $role = FastCGI::UNKNOWN_ROLE;\n" "\n" " /**\n" " * The flags component contains a bit that controls connection shutdown.\n" @@ -4788,23 +5249,19 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * If zero, the application closes the connection after responding to this request.\n" " * If not zero, the application does not close the connection after responding to this request;\n" " * the Web server retains responsibility for the connection.\n" - " *\n" - " * @var int\n" " */\n" - " protected $flags;\n" + " protected int $flags;\n" "\n" " /**\n" " * Reserved data, 5 bytes maximum\n" - " *\n" - " * @var string\n" " */\n" - " protected $reserved1;\n" + " protected string $reserved1;\n" "\n" " public function __construct(int $role = FastCGI::UNKNOWN_ROLE, int $flags = 0, string $reserved = '')\n" " {\n" - " $this->type = FastCGI::BEGIN_REQUEST;\n" - " $this->role = $role;\n" - " $this->flags = $flags;\n" + " $this->type = FastCGI::BEGIN_REQUEST;\n" + " $this->role = $role;\n" + " $this->flags = $flags;\n" " $this->reserved1 = $reserved;\n" " $this->setContentData($this->packPayload());\n" " }\n" @@ -4842,13 +5299,20 @@ static const char* swoole_library_source_core_fast_cgi_record_begin_request = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" + "\n" + " /** @phpstan-var false|array{role: int, flags: int, reserved: string} */\n" + " $payload = unpack('nrole/Cflags/a5reserved', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->role,\n" " $self->flags,\n" - " $self->reserved1\n" - " ] = array_values(unpack('nrole/Cflags/a5reserved', $data));\n" + " $self->reserved1,\n" + " ] = array_values($payload);\n" " }\n" "\n" " /** {@inheritdoc} */\n" @@ -4887,7 +5351,7 @@ static const char* swoole_library_source_core_fast_cgi_record_data = " */\n" "class Data extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::DATA;\n" " $this->setContentData($contentData);\n" @@ -4919,10 +5383,8 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = "{\n" " /**\n" " * The appStatus component is an application-level status code. Each role documents its usage of appStatus.\n" - " *\n" - " * @var int\n" " */\n" - " protected $appStatus = 0;\n" + " protected int $appStatus = 0;\n" "\n" " /**\n" " * The protocolStatus component is a protocol-level status code.\n" @@ -4936,27 +5398,20 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " * This happens when the application runs out of some resource, e.g. database connections.\n" " * FCGI_UNKNOWN_ROLE: rejecting a new request.\n" " * This happens when the Web server has specified a role that is unknown to the application.\n" - " *\n" - " * @var int\n" " */\n" - " protected $protocolStatus = FastCGI::REQUEST_COMPLETE;\n" + " protected int $protocolStatus = FastCGI::REQUEST_COMPLETE;\n" "\n" " /**\n" " * Reserved data, 3 bytes maximum\n" - " *\n" - " * @var string\n" " */\n" - " protected $reserved1;\n" + " protected string $reserved1;\n" "\n" - " public function __construct(\n" - " int $protocolStatus = FastCGI::REQUEST_COMPLETE,\n" - " int $appStatus = 0,\n" - " string $reserved = ''\n" - " ) {\n" - " $this->type = FastCGI::END_REQUEST;\n" + " public function __construct(int $protocolStatus = FastCGI::REQUEST_COMPLETE, int $appStatus = 0, string $reserved = '')\n" + " {\n" + " $this->type = FastCGI::END_REQUEST;\n" " $this->protocolStatus = $protocolStatus;\n" - " $this->appStatus = $appStatus;\n" - " $this->reserved1 = $reserved;\n" + " $this->appStatus = $appStatus;\n" + " $this->reserved1 = $reserved;\n" " $this->setContentData($this->packPayload());\n" " }\n" "\n" @@ -4992,13 +5447,20 @@ static const char* swoole_library_source_core_fast_cgi_record_end_request = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " protected static function unpackPayload($self, string $data): void\n" + " protected static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" + "\n" + " /** @phpstan-var false|array{appStatus: int, protocolStatus: int, reserved: string} */\n" + " $payload = unpack('NappStatus/CprotocolStatus/a3reserved', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " [\n" " $self->appStatus,\n" " $self->protocolStatus,\n" - " $self->reserved1\n" - " ] = array_values(unpack('NappStatus/CprotocolStatus/a3reserved', $data));\n" + " $self->reserved1,\n" + " ] = array_values($payload);\n" " }\n" "\n" " /** {@inheritdoc} */\n" @@ -5055,8 +5517,10 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values = " * Constructs a request\n" " *\n" " * @param array $keys List of keys to receive\n" + " *\n" + " * @phpstan-param list $keys\n" " */\n" - " public function __construct(array $keys = [])\n" + " public function __construct(array $keys)\n" " {\n" " parent::__construct(array_fill_keys($keys, ''));\n" " $this->type = FastCGI::GET_VALUES;\n" @@ -5103,8 +5567,10 @@ static const char* swoole_library_source_core_fast_cgi_record_get_values_result "{\n" " /**\n" " * Constructs a param request\n" + " *\n" + " * @phpstan-param array $values\n" " */\n" - " public function __construct(array $values = [])\n" + " public function __construct(array $values)\n" " {\n" " parent::__construct($values);\n" " $this->type = FastCGI::GET_VALUES_RESULT;\n" @@ -5135,7 +5601,7 @@ static const char* swoole_library_source_core_fast_cgi_record_stdin = " */\n" "class Stdin extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::STDIN;\n" " $this->setContentData($contentData);\n" @@ -5166,7 +5632,7 @@ static const char* swoole_library_source_core_fast_cgi_record_stdout = " */\n" "class Stdout extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::STDOUT;\n" " $this->setContentData($contentData);\n" @@ -5197,7 +5663,7 @@ static const char* swoole_library_source_core_fast_cgi_record_stderr = " */\n" "class Stderr extends Record\n" "{\n" - " public function __construct(string $contentData = '')\n" + " public function __construct(string $contentData)\n" " {\n" " $this->type = FastCGI::STDERR;\n" " $this->setContentData($contentData);\n" @@ -5233,22 +5699,18 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = "{\n" " /**\n" " * Type of the unrecognized management record.\n" - " *\n" - " * @var int\n" " */\n" - " protected $type1;\n" + " protected int $type1;\n" "\n" " /**\n" " * Reserved data, 7 bytes maximum\n" - " *\n" - " * @var string\n" " */\n" - " protected $reserved1;\n" + " protected string $reserved1;\n" "\n" - " public function __construct(int $type = 0, string $reserved = '')\n" + " public function __construct(int $type, string $reserved = '')\n" " {\n" - " $this->type = FastCGI::UNKNOWN_TYPE;\n" - " $this->type1 = $type;\n" + " $this->type = FastCGI::UNKNOWN_TYPE;\n" + " $this->type1 = $type;\n" " $this->reserved1 = $reserved;\n" " $this->setContentData($this->packPayload());\n" " }\n" @@ -5265,12 +5727,21 @@ static const char* swoole_library_source_core_fast_cgi_record_unknown_type = " * {@inheritdoc}\n" " * @param static $self\n" " */\n" - " public static function unpackPayload($self, string $data): void\n" + " public static function unpackPayload(Record $self, string $binaryData): void\n" " {\n" - " [$self->type1, $self->reserved1] = array_values(unpack('Ctype/a7reserved', $data));\n" + " assert($self instanceof self); // @phpstan-ignore function.alreadyNarrowedType,instanceof.alwaysTrue\n" + "\n" + " /** @phpstan-var false|array{type: int, reserved: string} */\n" + " $payload = unpack('Ctype/a7reserved', $binaryData);\n" + " if ($payload === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" + " [$self->type1, $self->reserved1] = array_values($payload);\n" " }\n" "\n" - " /** {@inheritdoc} */\n" + " /**\n" + " * {@inheritdoc}\n" + " */\n" " protected function packPayload(): string\n" " {\n" " return pack(\n" @@ -5295,9 +5766,18 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = "\n" "namespace Swoole\\FastCGI;\n" "\n" - "use DomainException;\n" - "use RuntimeException;\n" "use Swoole\\FastCGI;\n" + "use Swoole\\FastCGI\\Record\\AbortRequest;\n" + "use Swoole\\FastCGI\\Record\\BeginRequest;\n" + "use Swoole\\FastCGI\\Record\\Data;\n" + "use Swoole\\FastCGI\\Record\\EndRequest;\n" + "use Swoole\\FastCGI\\Record\\GetValues;\n" + "use Swoole\\FastCGI\\Record\\GetValuesResult;\n" + "use Swoole\\FastCGI\\Record\\Params;\n" + "use Swoole\\FastCGI\\Record\\Stderr;\n" + "use Swoole\\FastCGI\\Record\\Stdin;\n" + "use Swoole\\FastCGI\\Record\\Stdout;\n" + "use Swoole\\FastCGI\\Record\\UnknownType;\n" "\n" "/**\n" " * Utility class to simplify parsing of FastCGI protocol data.\n" @@ -5307,35 +5787,37 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " /**\n" " * Mapping of constants to the classes\n" " *\n" - " * @var array\n" - " */\n" - " protected static $classMapping = [\n" - " FastCGI::BEGIN_REQUEST => FastCGI\\Record\\BeginRequest::class,\n" - " FastCGI::ABORT_REQUEST => FastCGI\\Record\\AbortRequest::class,\n" - " FastCGI::END_REQUEST => FastCGI\\Record\\EndRequest::class,\n" - " FastCGI::PARAMS => FastCGI\\Record\\Params::class,\n" - " FastCGI::STDIN => FastCGI\\Record\\Stdin::class,\n" - " FastCGI::STDOUT => FastCGI\\Record\\Stdout::class,\n" - " FastCGI::STDERR => FastCGI\\Record\\Stderr::class,\n" - " FastCGI::DATA => FastCGI\\Record\\Data::class,\n" - " FastCGI::GET_VALUES => FastCGI\\Record\\GetValues::class,\n" - " FastCGI::GET_VALUES_RESULT => FastCGI\\Record\\GetValuesResult::class,\n" - " FastCGI::UNKNOWN_TYPE => FastCGI\\Record\\UnknownType::class,\n" + " * @phpstan-var array\n" + " */\n" + " protected static array $classMapping = [\n" + " FastCGI::BEGIN_REQUEST => BeginRequest::class,\n" + " FastCGI::ABORT_REQUEST => AbortRequest::class,\n" + " FastCGI::END_REQUEST => EndRequest::class,\n" + " FastCGI::PARAMS => Params::class,\n" + " FastCGI::STDIN => Stdin::class,\n" + " FastCGI::STDOUT => Stdout::class,\n" + " FastCGI::STDERR => Stderr::class,\n" + " FastCGI::DATA => Data::class,\n" + " FastCGI::GET_VALUES => GetValues::class,\n" + " FastCGI::GET_VALUES_RESULT => GetValuesResult::class,\n" + " FastCGI::UNKNOWN_TYPE => UnknownType::class,\n" " ];\n" "\n" " /**\n" " * Checks if the buffer contains a valid frame to parse\n" - " *\n" - " * @param string $buffer Binary buffer\n" " */\n" - " public static function hasFrame(string $buffer): bool\n" + " public static function hasFrame(string $binaryBuffer): bool\n" " {\n" - " $bufferLength = strlen($buffer);\n" + " $bufferLength = strlen($binaryBuffer);\n" " if ($bufferLength < FastCGI::HEADER_LEN) {\n" " return false;\n" " }\n" "\n" - " $fastInfo = unpack(FastCGI::HEADER_FORMAT, $buffer);\n" + " /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */\n" + " $fastInfo = unpack(FastCGI::HEADER_FORMAT, $binaryBuffer);\n" + " if ($fastInfo === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" + " }\n" " if ($bufferLength < FastCGI::HEADER_LEN + $fastInfo['contentLength'] + $fastInfo['paddingLength']) {\n" " return false;\n" " }\n" @@ -5346,28 +5828,30 @@ static const char* swoole_library_source_core_fast_cgi_frame_parser = " /**\n" " * Parses a frame from the binary buffer\n" " *\n" - " * @param string $buffer Binary buffer\n" - " *\n" " * @return Record One of the corresponding FastCGI record\n" " */\n" - " public static function parseFrame(string &$buffer): Record\n" + " public static function parseFrame(string &$binaryBuffer): Record\n" " {\n" - " $bufferLength = strlen($buffer);\n" + " $bufferLength = strlen($binaryBuffer);\n" " if ($bufferLength < FastCGI::HEADER_LEN) {\n" - " throw new RuntimeException('Not enough data in the buffer to parse');\n" + " throw new \\RuntimeException('Not enough data in the buffer to parse');\n" + " }\n" + " /** @phpstan-var false|array{version: int, type: int, requestId: int, contentLength: int, paddingLength: int} */\n" + " $recordHeader = unpack(FastCGI::HEADER_FORMAT, $binaryBuffer);\n" + " if ($recordHeader === false) {\n" + " throw new \\RuntimeException('Can not unpack data from the binary buffer');\n" " }\n" - " $recordHeader = unpack(FastCGI::HEADER_FORMAT, $buffer);\n" " $recordType = $recordHeader['type'];\n" " if (!isset(self::$classMapping[$recordType])) {\n" - " throw new DomainException(\"Invalid FastCGI record type {$recordType} received\");\n" + " throw new \\DomainException(\"Invalid FastCGI record type {$recordType} received\");\n" " }\n" "\n" " /** @var Record $className */\n" " $className = self::$classMapping[$recordType];\n" - " $record = $className::unpack($buffer);\n" + " $record = $className::unpack($binaryBuffer);\n" "\n" - " $offset = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\n" - " $buffer = substr($buffer, $offset);\n" + " $offset = FastCGI::HEADER_LEN + $record->getContentLength() + $record->getPaddingLength();\n" + " $binaryBuffer = substr($binaryBuffer, $offset);\n" "\n" " return $record;\n" " }\n" @@ -5389,27 +5873,24 @@ static const char* swoole_library_source_core_fast_cgi_message = "\n" "class Message\n" "{\n" - " /** @var array */\n" - " protected $params = [];\n" + " protected array $params = [];\n" "\n" - " /** @var string */\n" - " protected $body = '';\n" + " protected string $body = '';\n" "\n" - " /** @var string */\n" - " protected $error = '';\n" + " protected string $error = '';\n" "\n" " public function getParam(string $name): ?string\n" " {\n" " return $this->params[$name] ?? null;\n" " }\n" "\n" - " public function withParam(string $name, string $value): self\n" + " public function withParam(string $name, string $value): static\n" " {\n" " $this->params[$name] = $value;\n" " return $this;\n" " }\n" "\n" - " public function withoutParam(string $name): self\n" + " public function withoutParam(string $name): static\n" " {\n" " unset($this->params[$name]);\n" " return $this;\n" @@ -5420,13 +5901,13 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->params;\n" " }\n" "\n" - " public function withParams(array $params): self\n" + " public function withParams(array $params): static\n" " {\n" " $this->params = $params;\n" " return $this;\n" " }\n" "\n" - " public function withAddedParams(array $params): self\n" + " public function withAddedParams(array $params): static\n" " {\n" " $this->params = $params + $this->params;\n" " return $this;\n" @@ -5437,7 +5918,7 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->body;\n" " }\n" "\n" - " public function withBody($body): self\n" + " public function withBody(string|\\Stringable $body): self\n" " {\n" " $this->body = (string) $body;\n" " return $this;\n" @@ -5448,7 +5929,7 @@ static const char* swoole_library_source_core_fast_cgi_message = " return $this->error;\n" " }\n" "\n" - " public function withError(string $error): self\n" + " public function withError(string $error): static\n" " {\n" " $this->error = $error;\n" " return $this;\n" @@ -5474,18 +5955,18 @@ static const char* swoole_library_source_core_fast_cgi_request = "use Swoole\\FastCGI\\Record\\Params;\n" "use Swoole\\FastCGI\\Record\\Stdin;\n" "\n" - "class Request extends Message\n" + "class Request extends Message implements \\Stringable\n" "{\n" - " protected $keepConn = false;\n" + " protected bool $keepConn = false;\n" "\n" " public function __toString(): string\n" " {\n" - " $body = $this->getBody();\n" - " $beginRequestFrame = new BeginRequest(FastCGI::RESPONDER, ($this->keepConn ? FastCGI::KEEP_CONN : 0));\n" - " $paramsFrame = new Params($this->getParams());\n" - " $paramsEofFrame = new Params();\n" + " $body = $this->getBody();\n" + " $beginRequestFrame = new BeginRequest(FastCGI::RESPONDER, $this->keepConn ? FastCGI::KEEP_CONN : 0);\n" + " $paramsFrame = new Params($this->getParams());\n" + " $paramsEofFrame = new Params([]);\n" " if (empty($body)) {\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}}\";\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}\";\n" " } else {\n" " $stdinList = [];\n" " while (true) {\n" @@ -5496,9 +5977,9 @@ static const char* swoole_library_source_core_fast_cgi_request = " }\n" " $body = substr($body, $stdinLength);\n" " }\n" - " $stdinList[] = new Stdin();\n" - " $stdin = implode($stdinList);\n" - " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}}\";\n" + " $stdinList[] = new Stdin('');\n" + " $stdin = implode('', $stdinList);\n" + " $message = \"{$beginRequestFrame}{$paramsFrame}{$paramsEofFrame}{$stdin}\";\n" " }\n" " return $message;\n" " }\n" @@ -5529,20 +6010,22 @@ static const char* swoole_library_source_core_fast_cgi_response = "\n" "namespace Swoole\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" "use Swoole\\FastCGI\\Record\\EndRequest;\n" "use Swoole\\FastCGI\\Record\\Stderr;\n" "use Swoole\\FastCGI\\Record\\Stdout;\n" "\n" "class Response extends Message\n" "{\n" - " public function __construct(array $records = [])\n" + " /**\n" + " * @param array $records\n" + " */\n" + " public function __construct(array $records)\n" " {\n" " if (!static::verify($records)) {\n" - " throw new InvalidArgumentException('Bad records');\n" + " throw new \\InvalidArgumentException('Bad records');\n" " }\n" - " $body = '';\n" - " $error = '';\n" + "\n" + " $body = $error = '';\n" " foreach ($records as $record) {\n" " if ($record instanceof Stdout) {\n" " if ($record->getContentLength() > 0) {\n" @@ -5557,9 +6040,12 @@ static const char* swoole_library_source_core_fast_cgi_response = " $this->withBody($body)->withError($error);\n" " }\n" "\n" - " public static function verify(array $records): bool\n" + " /**\n" + " * @param array $records\n" + " */\n" + " protected static function verify(array $records): bool\n" " {\n" - " return !empty($records) && $records[count($records) - 1] instanceof EndRequest;\n" + " return !empty($records) && $records[array_key_last($records)] instanceof EndRequest;\n" " }\n" "}\n"; @@ -5577,30 +6063,28 @@ static const char* swoole_library_source_core_fast_cgi_http_request = "\n" "namespace Swoole\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" - "\n" "class HttpRequest extends Request\n" "{\n" - " protected $params = [\n" - " 'REQUEST_SCHEME' => 'http',\n" - " 'REQUEST_METHOD' => 'GET',\n" - " 'DOCUMENT_ROOT' => '',\n" - " 'SCRIPT_FILENAME' => '',\n" - " 'SCRIPT_NAME' => '',\n" - " 'DOCUMENT_URI' => '/',\n" - " 'REQUEST_URI' => '/',\n" - " 'QUERY_STRING' => '',\n" - " 'CONTENT_TYPE' => 'text/plain',\n" - " 'CONTENT_LENGTH' => '0',\n" + " protected array $params = [\n" + " 'REQUEST_SCHEME' => 'http',\n" + " 'REQUEST_METHOD' => 'GET',\n" + " 'DOCUMENT_ROOT' => '',\n" + " 'SCRIPT_FILENAME' => '',\n" + " 'SCRIPT_NAME' => '',\n" + " 'DOCUMENT_URI' => '/',\n" + " 'REQUEST_URI' => '/',\n" + " 'QUERY_STRING' => '',\n" + " 'CONTENT_TYPE' => 'text/plain',\n" + " 'CONTENT_LENGTH' => '0',\n" " 'GATEWAY_INTERFACE' => 'CGI/1.1',\n" - " 'SERVER_PROTOCOL' => 'HTTP/1.1',\n" - " 'SERVER_SOFTWARE' => 'swoole/' . SWOOLE_VERSION,\n" - " 'REMOTE_ADDR' => 'unknown',\n" - " 'REMOTE_PORT' => '0',\n" - " 'SERVER_ADDR' => 'unknown',\n" - " 'SERVER_PORT' => '0',\n" - " 'SERVER_NAME' => 'Swoole',\n" - " 'REDIRECT_STATUS' => '200',\n" + " 'SERVER_PROTOCOL' => 'HTTP/1.1',\n" + " 'SERVER_SOFTWARE' => 'swoole/' . SWOOLE_VERSION,\n" + " 'REMOTE_ADDR' => 'unknown',\n" + " 'REMOTE_PORT' => '0',\n" + " 'SERVER_ADDR' => 'unknown',\n" + " 'SERVER_PORT' => '0',\n" + " 'SERVER_NAME' => 'Swoole',\n" + " 'REDIRECT_STATUS' => '200',\n" " ];\n" "\n" " public function getScheme(): ?string\n" @@ -5688,7 +6172,8 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " $info = parse_url($uri);\n" " return $this->withRequestUri($uri)\n" " ->withDocumentUri($info['path'] ?? '')\n" - " ->withQueryString($info['query'] ?? '');\n" + " ->withQueryString($info['query'] ?? '')\n" + " ;\n" " }\n" "\n" " public function getDocumentUri(): ?string\n" @@ -5814,7 +6299,7 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " public function withProtocolVersion(string $protocolVersion): self\n" " {\n" " if (!is_numeric($protocolVersion)) {\n" - " throw new InvalidArgumentException('Protocol version must be numeric');\n" + " throw new \\InvalidArgumentException('Protocol version must be numeric');\n" " }\n" " $this->params['SERVER_PROTOCOL'] = \"HTTP/{$protocolVersion}\";\n" " return $this;\n" @@ -5952,7 +6437,7 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " {\n" " $headers = [];\n" " foreach ($this->params as $name => $value) {\n" - " if (strpos($name, 'HTTP_') === 0) {\n" + " if (str_starts_with($name, 'HTTP_')) {\n" " $headers[static::convertParamNameToHeaderName($name)] = $value;\n" " }\n" " }\n" @@ -5967,14 +6452,14 @@ static const char* swoole_library_source_core_fast_cgi_http_request = " return $this;\n" " }\n" "\n" - " /** @return $this */\n" - " public function withBody($body): Message\n" + " public function withBody(array|string|\\Stringable $body): self\n" " {\n" " if (is_array($body)) {\n" " $body = http_build_query($body);\n" " $this->withContentType('application/x-www-form-urlencoded');\n" " }\n" " parent::withBody($body);\n" + "\n" " return $this->withContentLength(strlen($body));\n" " }\n" "\n" @@ -6003,6 +6488,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = "\n" "namespace Swoole\\FastCGI;\n" "\n" + "use Swoole\\FastCGI\\Record\\EndRequest;\n" + "use Swoole\\FastCGI\\Record\\Stderr;\n" + "use Swoole\\FastCGI\\Record\\Stdout;\n" "use Swoole\\Http\\Status;\n" "\n" "class HttpResponse extends Response\n" @@ -6013,19 +6501,28 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " /** @var string */\n" " protected $reasonPhrase;\n" "\n" - " /** @var array */\n" - " protected $headers = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $headers = [];\n" "\n" - " /** @var array */\n" - " protected $headersMap = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $headersMap = [];\n" "\n" - " /** @var array */\n" - " protected $setCookieHeaderLines = [];\n" + " /**\n" + " * @var array\n" + " */\n" + " protected array $setCookieHeaderLines = [];\n" "\n" + " /**\n" + " * @param array $records\n" + " */\n" " public function __construct(array $records = [])\n" " {\n" " parent::__construct($records);\n" - " $body = (string) $this->getBody();\n" + " $body = $this->getBody();\n" " if (strlen($body) === 0) {\n" " return;\n" " }\n" @@ -6035,17 +6532,17 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return;\n" " }\n" " $headers = explode(\"\\r\\n\", $array[0]);\n" - " $body = $array[1];\n" + " $body = $array[1];\n" " foreach ($headers as $header) {\n" " $array = explode(':', $header, 2); // An array that contains the name and the value of an HTTP header.\n" " if (count($array) != 2) {\n" " continue; // Invalid HTTP header? Ignore it!\n" " }\n" - " $name = trim($array[0]);\n" + " $name = trim($array[0]);\n" " $value = trim($array[1]);\n" " if (strcasecmp($name, 'Status') === 0) {\n" - " $array = explode(' ', $value, 2); // An array that contains the status code (and the reason phrase).\n" - " $statusCode = $array[0];\n" + " $array = explode(' ', $value, 2); // An array that contains the status code (and the reason phrase).\n" + " $statusCode = $array[0];\n" " $reasonPhrase = $array[1] ?? null;\n" " } elseif (strcasecmp($name, 'Set-Cookie') === 0) {\n" " $this->withSetCookieHeaderLine($value);\n" @@ -6053,8 +6550,8 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " $this->withHeader($name, $value);\n" " }\n" " }\n" - " $statusCode = (int) ($statusCode ?? Status::OK);\n" - " $reasonPhrase = (string) ($reasonPhrase ?? Status::getReasonPhrase($statusCode));\n" + " $statusCode = (int) ($statusCode ?? Status::OK);\n" + " $reasonPhrase = $reasonPhrase ?? Status::getReasonPhrase($statusCode);\n" " $this->withStatusCode($statusCode)->withReasonPhrase($reasonPhrase);\n" " $this->withBody($body);\n" " }\n" @@ -6087,6 +6584,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return $name ? $this->headers[$name] : null;\n" " }\n" "\n" + " /**\n" + " * @return array\n" + " */\n" " public function getHeaders(): array\n" " {\n" " return $this->headers;\n" @@ -6094,11 +6594,14 @@ static const char* swoole_library_source_core_fast_cgi_http_response = "\n" " public function withHeader(string $name, string $value): self\n" " {\n" - " $this->headers[$name] = $value;\n" + " $this->headers[$name] = $value;\n" " $this->headersMap[strtolower($name)] = $name;\n" " return $this;\n" " }\n" "\n" + " /**\n" + " * @param array $headers\n" + " */\n" " public function withHeaders(array $headers): self\n" " {\n" " foreach ($headers as $name => $value) {\n" @@ -6107,6 +6610,9 @@ static const char* swoole_library_source_core_fast_cgi_http_response = " return $this;\n" " }\n" "\n" + " /**\n" + " * @return array\n" + " */\n" " public function getSetCookieHeaderLines(): array\n" " {\n" " return $this->setCookieHeaderLines;\n" @@ -6133,7 +6639,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" "namespace Swoole\\Coroutine\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" + "use Swoole\\Constant;\n" "use Swoole\\Coroutine\\FastCGI\\Client\\Exception;\n" "use Swoole\\Coroutine\\Socket;\n" "use Swoole\\FastCGI\\FrameParser;\n" @@ -6145,48 +6651,43 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" "class Client\n" "{\n" - " /** @var int */\n" - " protected $af;\n" + " protected int $af;\n" "\n" - " /** @var string */\n" - " protected $host;\n" + " protected string $host;\n" "\n" - " /** @var int */\n" - " protected $port;\n" + " protected int $port;\n" "\n" - " /** @var bool */\n" - " protected $ssl;\n" + " protected bool $ssl;\n" "\n" - " /** @var Socket */\n" - " protected $socket;\n" + " protected ?Socket $socket;\n" "\n" " public function __construct(string $host, int $port = 0, bool $ssl = false)\n" " {\n" " if (stripos($host, 'unix:/') === 0) {\n" " $this->af = AF_UNIX;\n" - " $host = '/' . ltrim(substr($host, strlen('unix:/')), '/');\n" - " $port = 0;\n" - " } elseif (strpos($host, ':') !== false) {\n" + " $host = '/' . ltrim(substr($host, strlen('unix:/')), '/');\n" + " $port = 0;\n" + " } elseif (str_contains($host, ':')) {\n" " $this->af = AF_INET6;\n" " } else {\n" " $this->af = AF_INET;\n" " }\n" " $this->host = $host;\n" " $this->port = $port;\n" - " $this->ssl = $ssl;\n" + " $this->ssl = $ssl;\n" " }\n" "\n" " /**\n" + " * @return ($request is HttpRequest ? HttpResponse : Response)\n" " * @throws Exception\n" - " * @return HttpResponse|Response\n" " */\n" " public function execute(Request $request, float $timeout = -1): Response\n" " {\n" - " if (!$this->socket) {\n" + " if (!isset($this->socket)) {\n" " $this->socket = $socket = new Socket($this->af, SOCK_STREAM, IPPROTO_IP);\n" " $socket->setProtocol([\n" - " 'open_ssl' => $this->ssl,\n" - " 'open_fastcgi_protocol' => true,\n" + " Constant::OPTION_OPEN_SSL => $this->ssl,\n" + " Constant::OPTION_OPEN_FASTCGI_PROTOCOL => true,\n" " ]);\n" " if (!$socket->connect($this->host, $this->port, $timeout)) {\n" " $this->ioException();\n" @@ -6200,33 +6701,17 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " }\n" " $records = [];\n" " while (true) {\n" - " if (SWOOLE_VERSION_ID < 40500) {\n" - " $recvData = '';\n" - " while (true) {\n" - " $tmp = $socket->recv(8192, $timeout);\n" - " if (!$tmp) {\n" - " if ($tmp === '') {\n" - " $this->ioException(SOCKET_ECONNRESET);\n" - " }\n" - " $this->ioException();\n" - " }\n" - " $recvData .= $tmp;\n" - " if (FrameParser::hasFrame($recvData)) {\n" - " break;\n" - " }\n" - " }\n" - " } else {\n" - " $recvData = $socket->recvPacket($timeout);\n" - " if (!$recvData) {\n" - " if ($recvData === '') {\n" - " $this->ioException(SOCKET_ECONNRESET);\n" - " }\n" - " $this->ioException();\n" - " }\n" - " if (!FrameParser::hasFrame($recvData)) {\n" - " $this->ioException(SOCKET_EPROTO);\n" + " $recvData = $socket->recvPacket($timeout);\n" + " if (!$recvData) {\n" + " if ($recvData === '') {\n" + " $this->ioException(SOCKET_ECONNRESET);\n" " }\n" + " $this->ioException();\n" " }\n" + " if (!FrameParser::hasFrame($recvData)) {\n" + " $this->ioException(SOCKET_EPROTO);\n" + " }\n" + "\n" " do {\n" " $records[] = $record = FrameParser::parseFrame($recvData);\n" " } while (strlen($recvData) !== 0);\n" @@ -6235,27 +6720,24 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " $this->socket->close();\n" " $this->socket = null;\n" " }\n" - " switch (true) {\n" - " case $request instanceof HttpRequest:\n" - " return new HttpResponse($records);\n" - " default:\n" - " return new Response($records);\n" - " }\n" + " // @phpstan-ignore argument.type,argument.type\n" + " return ($request instanceof HttpRequest) ? new HttpResponse($records) : new Response($records);\n" " }\n" " }\n" - " /* never here */\n" - " exit(1);\n" + "\n" + " // Code execution should never reach here. However, we still put an exit() statement here for safe purpose.\n" + " exit(1); // @phpstan-ignore deadCode.unreachable\n" " }\n" "\n" " public static function parseUrl(string $url): array\n" " {\n" - " $url = parse_url($url);\n" + " $url = parse_url($url);\n" " $host = $url['host'] ?? '';\n" " $port = $url['port'] ?? 0;\n" " if (empty($host)) {\n" " $host = $url['path'] ?? '';\n" " if (empty($host)) {\n" - " throw new InvalidArgumentException('Invalid url');\n" + " throw new \\InvalidArgumentException('Invalid url');\n" " }\n" " $host = \"unix:/{$host}\";\n" " }\n" @@ -6264,15 +6746,15 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = "\n" " public static function call(string $url, string $path, $data = '', float $timeout = -1): string\n" " {\n" - " $client = new Client(...static::parseUrl($url));\n" - " $pathInfo = parse_url($path);\n" - " $path = $pathInfo['path'] ?? '';\n" - " $root = dirname($path);\n" - " $scriptName = '/' . basename($path);\n" + " $client = new Client(...static::parseUrl($url));\n" + " $pathInfo = parse_url($path);\n" + " $path = $pathInfo['path'] ?? '';\n" + " $root = dirname($path);\n" + " $scriptName = '/' . basename($path);\n" " $documentUri = $scriptName;\n" - " $query = $pathInfo['query'] ?? '';\n" - " $requestUri = $query ? \"{$documentUri}?{$query}\" : $documentUri;\n" - " $request = new HttpRequest();\n" + " $query = $pathInfo['query'] ?? '';\n" + " $requestUri = $query ? \"{$documentUri}?{$query}\" : $documentUri;\n" + " $request = new HttpRequest();\n" " $request->withDocumentRoot($root)\n" " ->withScriptFilename($path)\n" " ->withScriptName($documentUri)\n" @@ -6280,7 +6762,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " ->withRequestUri($requestUri)\n" " ->withQueryString($query)\n" " ->withBody($data)\n" - " ->withMethod($request->getContentLength() === 0 ? 'GET' : 'POST');\n" + " ->withMethod($request->getContentLength() === 0 ? 'GET' : 'POST')\n" + " ;\n" " $response = $client->execute($request, $timeout);\n" " return $response->getBody();\n" " }\n" @@ -6290,7 +6773,7 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_client = " $socket = $this->socket;\n" " if ($errno !== null) {\n" " $socket->errCode = $errno;\n" - " $socket->errMsg = swoole_strerror($errno);\n" + " $socket->errMsg = swoole_strerror($errno);\n" " }\n" " $socket->close();\n" " $this->socket = null;\n" @@ -6330,10 +6813,11 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = "\n" "namespace Swoole\\Coroutine\\FastCGI;\n" "\n" - "use InvalidArgumentException;\n" "use Swoole\\FastCGI\\HttpRequest;\n" "use Swoole\\FastCGI\\HttpResponse;\n" "use Swoole\\Http;\n" + "use Swoole\\Http\\Request as SwooleHttpRequest;\n" + "use Swoole\\Http\\Response as SwooleHttpResponse;\n" "\n" "class Proxy\n" "{\n" @@ -6364,8 +6848,8 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " public function __construct(string $url, string $documentRoot = '/')\n" " {\n" " [$this->host, $this->port] = Client::parseUrl($url);\n" - " $this->documentRoot = $documentRoot;\n" - " $this->staticFileFilter = [$this, 'staticFileFiltrate'];\n" + " $this->documentRoot = $documentRoot;\n" + " $this->staticFileFilter = [$this, 'staticFileFiltrate'];\n" " }\n" "\n" " public function withTimeout(float $timeout): self\n" @@ -6426,67 +6910,60 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " return $this;\n" " }\n" "\n" - " public function translateRequest($userRequest): HttpRequest\n" + " public function translateRequest(SwooleHttpRequest $userRequest): HttpRequest\n" " {\n" - " $request = new HttpRequest();\n" - " if ($userRequest instanceof \\Swoole\\Http\\Request) {\n" - " $server = $userRequest->server;\n" - " $headers = $userRequest->header;\n" - " $pathInfo = $userRequest->server['path_info'];\n" - " $pathInfo = '/' . (ltrim($pathInfo, '/'));\n" - " if (strlen($this->index) !== 0) {\n" - " $extension = pathinfo($pathInfo, PATHINFO_EXTENSION);\n" - " if (empty($extension)) {\n" - " $pathInfo = rtrim($pathInfo, '/') . '/' . $this->index;\n" - " }\n" - " }\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" @@ -6498,28 +6975,29 @@ static const char* swoole_library_source_core_coroutine_fast_cgi_proxy = " return;\n" " }\n" " }\n" - " $client = new Client($this->host, $this->port);\n" - " $response = $client->execute($request, $this->timeout);\n" + " $response = (new Client($this->host, $this->port))->execute($request, $this->timeout);\n" " $this->translateResponse($response, $userResponse);\n" " }\n" "\n" - " /* @return bool ['hit' => true, 'miss' => false] */\n" - " public function staticFileFiltrate(HttpRequest $request, $userResponse): bool\n" + " /**\n" + " * Send content of a static file to the client, if the file is accessible and is not a PHP file.\n" + " *\n" + " * @return bool True if the file doesn't have an extension of 'php', false otherwise. Note that the file may not be\n" + " * accessible even the return value is true.\n" + " */\n" + " public function staticFileFiltrate(HttpRequest $request, SwooleHttpResponse $userResponse): bool\n" " {\n" - " if ($userResponse instanceof \\Swoole\\Http\\Response) {\n" - " $extension = pathinfo($request->getScriptFilename(), PATHINFO_EXTENSION);\n" - " if ($extension !== 'php') {\n" - " $realPath = realpath($request->getScriptFilename());\n" - " if (!$realPath || strpos($realPath, $this->documentRoot) !== 0 || !is_file($realPath)) {\n" - " $userResponse->status(Http\\Status::NOT_FOUND);\n" - " } else {\n" - " $userResponse->sendfile($realPath);\n" - " }\n" - " return true;\n" + " $extension = pathinfo($request->getScriptFilename(), PATHINFO_EXTENSION);\n" + " if ($extension !== 'php') {\n" + " $realPath = realpath($request->getScriptFilename());\n" + " if (!$realPath || !str_starts_with($realPath, $this->documentRoot) || !is_file($realPath)) {\n" + " $userResponse->status(Http\\Status::NOT_FOUND);\n" + " } else {\n" + " $userResponse->sendfile($realPath);\n" " }\n" - " return false;\n" + " return true;\n" " }\n" - " throw new InvalidArgumentException('Not supported on ' . get_class($userResponse));\n" + " return false;\n" " }\n" "}\n"; @@ -6538,6 +7016,7 @@ static const char* swoole_library_source_core_process_manager = "namespace Swoole\\Process;\n" "\n" "use Swoole\\Constant;\n" + "\n" "use function Swoole\\Coroutine\\run;\n" "\n" "class Manager\n" @@ -6620,7 +7099,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" @@ -6634,292 +7113,2445 @@ 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" - " 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" - " '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_STRING = 32;\n" + "\n" + " public const SIZE_OF_ZEND_OBJECT = 56;\n" + "\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" - " foreach ($input_options as $k => $v) {\n" - " if (!array_key_exists(strtolower($k), $const_options)) {\n" - " //TODO throw exception\n" - " trigger_error(\"unsupported option [{$k}]\", E_USER_WARNING);\n" - " debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\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" + " );\n" "\n" - " public static function onWorkerStart(Server $server, int $workerId)\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" + " $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" - " $out = implode(\"\\n\", $lines);\n" - " file_put_contents($server->setting['stats_file'], $out);\n" - " });\n" - " }\n" - " }\n" + " return self::json($bt);\n" + " }\n" + " );\n" "\n" - " public static function onWorkerExit(Server $server, int $workerId)\n" - " {\n" - " if ($server->stats_timer) {\n" - " Timer::clear($server->stats_timer);\n" - " $server->stats_timer = null;\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" - " public static function onWorkerStop(Server $server, int $workerId)\n" - " {\n" - " }\n" - "}\n"; - -static const char* swoole_library_source_core_coroutine_functions = + " $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" - "/**\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" + " $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" - "declare(strict_types=1);\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" - "namespace Swoole\\Coroutine;\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" - "use Swoole\\Coroutine;\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" - "function batch(array $tasks, float $timeout = -1): array\n" - "{\n" - " $wg = new WaitGroup(count($tasks));\n" - " foreach ($tasks as $id => $task) {\n" - " Coroutine::create(function () use ($wg, &$tasks, $id, $task) {\n" - " $tasks[$id] = null;\n" - " $tasks[$id] = $task();\n" - " $wg->done();\n" - " });\n" - " }\n" - " $wg->wait($timeout);\n" - " return $tasks;\n" - "}\n" + " $server->addCommand(\n" + " 'getpid',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(['pid' => posix_getpid()])\n" + " );\n" "\n" - "function parallel(int $n, callable $fn): void\n" - "{\n" - " $count = $n;\n" - " $wg = new WaitGroup($n);\n" - " while ($count--) {\n" - " Coroutine::create(function () use ($fn, $wg) {\n" - " $fn();\n" - " $wg->done();\n" - " });\n" - " }\n" - " $wg->wait();\n" - "}\n" + " $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" - "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" - " $wg->done();\n" - " });\n" - " }\n" - " $wg->wait($timeout);\n" - " return $list;\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" - "function deadlock_check()\n" - "{\n" - " $all_coroutines = Coroutine::listCoroutines();\n" - " $count = Coroutine::stats()['coroutine_num'];\n" - " echo\n" - " \"\\n===================================================================\",\n" - " \"\\n [FATAL ERROR]: all coroutines (count: {$count}) are asleep - deadlock!\",\n" - " \"\\n===================================================================\\n\";\n" + " $server->addCommand('get_resources', $accepted_process_types, [self::class, 'handlerGetResources']);\n" "\n" - " $options = Coroutine::getOptions();\n" - " if (empty($options['deadlock_check_disable_trace'])) {\n" - " $index = 0;\n" - " $limit = empty($options['deadlock_check_limit']) ? 32 : intval($options['deadlock_check_limit']);\n" - " $depth = empty($options['deadlock_check_depth']) ? 32 : intval($options['deadlock_check_depth']);\n" - " foreach ($all_coroutines as $cid) {\n" - " echo \"\\n [Coroutine-{$cid}]\";\n" - " echo \"\\n--------------------------------------------------------------------\\n\";\n" - " echo Coroutine::printBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, $depth);\n" - " echo \"\\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" - " //limit the number of maximum outputs\n" - " if ($index > $limit) {\n" - " break;\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" - "}\n"; - -static const char* swoole_library_source_ext_curl = + " );\n" "\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" + " $server->addCommand(\n" + " 'get_declared_interfaces',\n" + " $accepted_process_types,\n" + " fn (Server $server, string $msg) => self::json(get_declared_interfaces())\n" + " );\n" "\n" - "/* @noinspection PhpComposerExtensionStubsInspection */\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" - "declare(strict_types=1);\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" - "function swoole_curl_init(string $url = ''): Swoole\\Curl\\Handler\n" - "{\n" - " return new Swoole\\Curl\\Handler($url);\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" + " 'iouring_entries' => true,\n" + " 'iouring_workers' => true,\n" + " 'iouring_flag' => true,\n" + " ];\n" + "\n" + " public const COROUTINE_OPTIONS = [\n" + " 'max_coro_num' => true,\n" + " 'max_coroutine' => true,\n" + " 'enable_deadlock_check' => true,\n" + " 'hook_flags' => true,\n" + " 'enable_preemptive_scheduler' => true,\n" + " 'c_stack_size' => true,\n" + " 'stack_size' => true,\n" + " 'name_resolver' => true,\n" + " 'dns_cache_expire' => true,\n" + " 'dns_cache_capacity' => true,\n" + " 'max_concurrency' => true,\n" + " ];\n" + "\n" + " public const HELPER_OPTIONS = [\n" + " 'stats_file' => true,\n" + " 'stats_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" + " trigger_error(\"unsupported option [{$k}]\", E_USER_WARNING);\n" + " debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);\n" + " }\n" + " }\n" + " }\n" + "\n" + " public static function onBeforeStart(Server $server): 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" + " $url = $this->baseUrl . '/v1/agent/service/deregister/' . $this->getServiceId(\n" + " $name,\n" + " $ip,\n" + " $port\n" + " );\n" + " $r = request($url, 'PUT');\n" + " return $this->checkResponse($r);\n" + " }\n" + "\n" + " public function enableMaintenanceMode(string $name, string $ip, int $port): bool\n" + " {\n" + " $url = $this->baseUrl . '/v1/agent/service/maintenance/' . $this->getServiceId(\n" + " $name,\n" + " $ip,\n" + " $port\n" + " );\n" + " $r = request($url, 'PUT');\n" + " return $this->checkResponse($r);\n" + " }\n" + "\n" + " public function getCluster(string $name): ?Cluster\n" + " {\n" + " $url = $this->baseUrl . '/v1/catalog/service/' . $this->prefix . $name;\n" + " $r = get($url);\n" + " if (!$this->checkResponse($r)) {\n" + " return null;\n" + " }\n" + " $list = json_decode($r->getBody(), null, 512, JSON_THROW_ON_ERROR);\n" + " if (empty($list)) {\n" + " return null;\n" + " }\n" + " $cluster = new Cluster();\n" + " foreach ($list as $li) {\n" + " $cluster->add($li->ServiceAddress, $li->ServicePort, $li->ServiceWeights->Passing);\n" + " }\n" + " return $cluster;\n" + " }\n" + "\n" + " private function getServiceId(string $name, string $ip, int $port): string\n" + " {\n" + " return $this->prefix . $name . \"_{$ip}:{$port}\";\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_thread_pool = + "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Thread;\n" + "\n" + "use PhpParser\\Error;\n" + "use PhpParser\\ParserFactory;\n" + "use Swoole\\Thread;\n" + "\n" + "/**\n" + " * @since 6.0.0-beta\n" + " */\n" + "class Pool\n" + "{\n" + " private array $threads = [];\n" + "\n" + " private string $autoloader = '';\n" + "\n" + " private string $classDefinitionFile = '';\n" + "\n" + " private string $runnableClass = '';\n" + "\n" + " private int $threadNum = 0;\n" + "\n" + " private string $proxyFile;\n" + "\n" + " private array $arguments = [];\n" + "\n" + " private object $running;\n" + "\n" + " private object $queue;\n" + "\n" + " private array $indexes = [];\n" + "\n" + " public function __construct(string $runnableClass, int $threadNum)\n" + " {\n" + " if ($threadNum <= 0) {\n" + " throw new \\Exception('threadNum must be greater than 0');\n" + " }\n" + " $this->runnableClass = $runnableClass;\n" + " $this->threadNum = $threadNum;\n" + " }\n" + "\n" + " public function withArguments(...$arguments): static\n" + " {\n" + " $this->arguments = $arguments;\n" + " return $this;\n" + " }\n" + "\n" + " public function withAutoloader(string $autoloader): static\n" + " {\n" + " $this->autoloader = $autoloader;\n" + " return $this;\n" + " }\n" + "\n" + " public function withClassDefinitionFile(string $classDefinitionFile): static\n" + " {\n" + " $this->classDefinitionFile = $classDefinitionFile;\n" + " return $this;\n" + " }\n" + "\n" + " /**\n" + " * @throws \\ReflectionException\n" + " */\n" + " public function start(): void\n" + " {\n" + " if (empty($this->classDefinitionFile) and class_exists($this->runnableClass, false)) {\n" + " $file = (new \\ReflectionClass($this->runnableClass))->getFileName();\n" + " if (!$this->isValidPhpFile($file)) {\n" + " throw new \\Exception('class definition file must not contain any expressions.');\n" + " }\n" + " $this->classDefinitionFile = $file;\n" + " } elseif ($this->classDefinitionFile) {\n" + " require_once $this->classDefinitionFile;\n" + " }\n" + "\n" + " if (!class_exists($this->runnableClass)) {\n" + " throw new \\Exception(\"class `{$this->runnableClass}` not found\");\n" + " }\n" + "\n" + " if (!is_subclass_of($this->runnableClass, Runnable::class)) {\n" + " throw new \\Exception(\"class `{$this->runnableClass}` must implements Thread\\\\Runnable\");\n" + " }\n" + "\n" + " if (empty($this->autoloader)) {\n" + " $include_files = get_included_files();\n" + " foreach ($include_files as $file) {\n" + " if (str_ends_with($file, 'vendor/autoload.php')) {\n" + " $this->autoloader = $file;\n" + " break;\n" + " }\n" + " }\n" + " }\n" + "\n" + " if ($this->autoloader) {\n" + " $this->proxyFile = dirname($this->autoloader) . '/thread_runner.php';\n" + " } else {\n" + " $this->proxyFile = dirname($this->classDefinitionFile) . '/thread_runner.php';\n" + " }\n" + "\n" + " if (!is_file($this->proxyFile)) {\n" + " $script = 'run($threadArguments); }' . PHP_EOL;\n" + " $script .= 'finally { $queue->push($threadId, Swoole\\Thread\\Queue::NOTIFY_ONE); }' . PHP_EOL;\n" + " $script .= PHP_EOL;\n" + " file_put_contents($this->proxyFile, $script);\n" + " }\n" + "\n" + " $this->queue = new Queue();\n" + " $this->running = new Atomic(1);\n" + "\n" + " for ($index = 0; $index < $this->threadNum; $index++) {\n" + " $this->createThread($index);\n" + " }\n" + "\n" + " while ($this->running->get()) {\n" + " $threadId = $this->queue->pop(-1);\n" + " $thread = $this->threads[$threadId];\n" + " $index = $this->indexes[$threadId];\n" + " $thread->join();\n" + " unset($this->threads[$threadId], $this->indexes[$threadId]);\n" + "\n" + " $this->createThread($index);\n" + " }\n" + "\n" + " foreach ($this->threads as $thread) {\n" + " $thread->join();\n" + " }\n" + " }\n" + "\n" + " public function shutdown(): void\n" + " {\n" + " $this->running->set(0);\n" + " }\n" + "\n" + " protected function isValidPhpFile($filePath): bool\n" + " {\n" + " $allowedNodeTypes = [\n" + " \\PhpParser\\Node\\Stmt\\Class_::class,\n" + " \\PhpParser\\Node\\Stmt\\Const_::class,\n" + " \\PhpParser\\Node\\Stmt\\Use_::class,\n" + " \\PhpParser\\Node\\Stmt\\Namespace_::class,\n" + " \\PhpParser\\Node\\Stmt\\Declare_::class,\n" + " ];\n" + "\n" + " $parser = (new ParserFactory())->createForNewestSupportedVersion();\n" + " try {\n" + " $code = file_get_contents($filePath);\n" + " $stmts = $parser->parse($code);\n" + " $skipLine = -1;\n" + " foreach ($stmts as $stmt) {\n" + " $isAllowed = false;\n" + " foreach ($allowedNodeTypes as $allowedNodeType) {\n" + " if ($stmt instanceof $allowedNodeType) {\n" + " $isAllowed = true;\n" + " break;\n" + " }\n" + " }\n" + " if (!$isAllowed) {\n" + " if ($stmt->getLine() == $skipLine) {\n" + " continue;\n" + " }\n" + " return false;\n" + " }\n" + " }\n" + " } catch (Error $error) {\n" + " return false;\n" + " }\n" + " return true;\n" + " }\n" + "\n" + " protected function createThread($index): void\n" + " {\n" + " $thread = new Thread($this->proxyFile,\n" + " $this->autoloader,\n" + " $this->runnableClass,\n" + " $this->queue,\n" + " $this->classDefinitionFile,\n" + " $this->running,\n" + " $index,\n" + " ...$this->arguments\n" + " );\n" + " $this->indexes[$thread->id] = $index;\n" + " $this->threads[$thread->id] = $thread;\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_thread_runnable = + "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Thread;\n" + "\n" + "/**\n" + " * @since 6.0.0-beta\n" + " */\n" + "abstract class Runnable\n" + "{\n" + " protected Atomic $running;\n" + "\n" + " protected int $id;\n" + "\n" + " public function __construct($running, $index)\n" + " {\n" + " $this->running = $running;\n" + " $this->id = $index;\n" + " }\n" + "\n" + " abstract public function run(array $args): void;\n" + "\n" + " protected function isRunning(): bool\n" + " {\n" + " return $this->running->get() === 1;\n" + " }\n" + "\n" + " protected function shutdown(): void\n" + " {\n" + " $this->running->set(0);\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_core_coroutine_functions = + "\n" + "/**\n" + " * This file is part of Swoole.\n" + " *\n" + " * @link https://www.swoole.com\n" + " * @contact team@swoole.com\n" + " * @license https://github.com/swoole/library/blob/master/LICENSE\n" + " */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "namespace Swoole\\Coroutine;\n" + "\n" + "use Swoole\\Coroutine;\n" + "\n" + "function run(callable $fn, ...$args)\n" + "{\n" + " $s = new Scheduler();\n" + " $options = Coroutine::getOptions();\n" + " if (!isset($options['hook_flags'])) {\n" + " $s->set(['hook_flags' => SWOOLE_HOOK_ALL]);\n" + " }\n" + " $s->add($fn, ...$args);\n" + " return $s->start();\n" + "}\n" + "\n" + "function go(callable $fn, ...$args)\n" + "{\n" + " return Coroutine::create($fn, ...$args);\n" + "}\n" + "\n" + "function defer(callable $fn)\n" + "{\n" + " Coroutine::defer($fn);\n" + "}\n" + "\n" + "function batch(array $tasks, float $timeout = -1): array\n" + "{\n" + " $wg = new WaitGroup(count($tasks));\n" + " foreach ($tasks as $id => $task) {\n" + " Coroutine::create(function () use ($wg, &$tasks, $id, $task) {\n" + " $tasks[$id] = null;\n" + " $tasks[$id] = $task();\n" + " $wg->done();\n" + " });\n" + " }\n" + " $wg->wait($timeout);\n" + " return $tasks;\n" + "}\n" + "\n" + "function parallel(int $n, callable $fn): void\n" + "{\n" + " $count = $n;\n" + " $wg = new WaitGroup($n);\n" + " while ($count--) {\n" + " Coroutine::create(function () use ($fn, $wg) {\n" + " $fn();\n" + " $wg->done();\n" + " });\n" + " }\n" + " $wg->wait();\n" + "}\n" + "\n" + "/**\n" + " * Applies the callback to the elements of the given list.\n" + " *\n" + " * The callback function takes on two parameters. The list parameter's value being the first, and the key/index second.\n" + " * Each callback runs in a new coroutine, allowing the list to be processed in parallel.\n" + " *\n" + " * @param array $list A list of key/value paired input data.\n" + " * @param callable $fn The callback function to apply to each item on the list. The callback takes on two parameters.\n" + " * The list parameter's value being the first, and the key/index second.\n" + " * @param float $timeout > 0 means waiting for the specified number of seconds. other means no waiting.\n" + " * @return array Returns an array containing the results of applying the callback function to the corresponding value\n" + " * and key of the list (used as arguments for the callback). The returned array will preserve the keys of\n" + " * the list.\n" + " */\n" + "function map(array $list, callable $fn, float $timeout = -1): array\n" + "{\n" + " $wg = new WaitGroup(count($list));\n" + " foreach ($list as $id => $elem) {\n" + " Coroutine::create(function () use ($wg, &$list, $id, $elem, $fn): void {\n" + " $list[$id] = null;\n" + " $list[$id] = $fn($elem, $id);\n" + " $wg->done();\n" + " });\n" + " }\n" + " $wg->wait($timeout);\n" + " return $list;\n" + "}\n" + "\n" + "function deadlock_check()\n" + "{\n" + " $all_coroutines = Coroutine::listCoroutines();\n" + " $count = Coroutine::stats()['coroutine_num'];\n" + " echo \"\\n===================================================================\",\n" + " \"\\n [FATAL ERROR]: all coroutines (count: {$count}) are asleep - deadlock!\",\n" + " \"\\n===================================================================\\n\";\n" + "\n" + " $options = Coroutine::getOptions();\n" + " if (empty($options['deadlock_check_disable_trace'])) {\n" + " $index = 0;\n" + " $limit = empty($options['deadlock_check_limit']) ? 32 : intval($options['deadlock_check_limit']);\n" + " $depth = empty($options['deadlock_check_depth']) ? 32 : intval($options['deadlock_check_depth']);\n" + " foreach ($all_coroutines as $cid) {\n" + " echo \"\\n [Coroutine-{$cid}]\";\n" + " echo \"\\n--------------------------------------------------------------------\\n\";\n" + " echo Coroutine::printBackTrace($cid, DEBUG_BACKTRACE_IGNORE_ARGS, $depth);\n" + " echo \"\\n\";\n" + " $index++;\n" + " // limit the number of maximum outputs\n" + " if ($index >= $limit) {\n" + " break;\n" + " }\n" + " }\n" + " }\n" + "}\n"; + +static const char* swoole_library_source_ext_curl = + "\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" + "/* @noinspection PhpComposerExtensionStubsInspection */\n" + "\n" + "declare(strict_types=1);\n" + "\n" + "function swoole_curl_init(string $url = ''): Swoole\\Curl\\Handler\n" + "{\n" + " return new Swoole\\Curl\\Handler($url);\n" "}\n" "\n" "function swoole_curl_setopt(Swoole\\Curl\\Handler $obj, int $opt, $value): bool\n" @@ -6946,42 +9578,32 @@ static const char* swoole_library_source_ext_curl = "{\n" " $info = $obj->getInfo();\n" " if (is_array($info) and $opt) {\n" - " switch ($opt) {\n" - " case CURLINFO_EFFECTIVE_URL:\n" - " return $info['url'];\n" - " case CURLINFO_HTTP_CODE:\n" - " return $info['http_code'];\n" - " case CURLINFO_CONTENT_TYPE:\n" - " return $info['content_type'];\n" - " case CURLINFO_REDIRECT_COUNT:\n" - " return $info['redirect_count'];\n" - " case CURLINFO_REDIRECT_URL:\n" - " return $info['redirect_url'];\n" - " case CURLINFO_TOTAL_TIME:\n" - " return $info['total_time'];\n" - " case CURLINFO_STARTTRANSFER_TIME:\n" - " return $info['starttransfer_time'];\n" - " case CURLINFO_SIZE_DOWNLOAD:\n" - " return $info['size_download'];\n" - " case CURLINFO_SPEED_DOWNLOAD:\n" - " return $info['speed_download'];\n" - " case CURLINFO_REDIRECT_TIME:\n" - " return $info['redirect_time'];\n" - " case CURLINFO_HEADER_SIZE:\n" - " return $info['header_size'];\n" - " 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" @@ -6991,9 +9613,9 @@ static const char* swoole_library_source_ext_curl = " return $obj->reset();\n" "}\n" "\n" - "function swoole_curl_close(Swoole\\Curl\\Handler $obj)\n" + "function swoole_curl_close(Swoole\\Curl\\Handler $obj): void\n" "{\n" - " return $obj->close();\n" + " $obj->close();\n" "}\n" "\n" "function swoole_curl_multi_getcontent(Swoole\\Curl\\Handler $obj)\n" @@ -7028,12 +9650,12 @@ static const char* swoole_library_source_ext_sockets = "function swoole_socket_read(Socket $socket, int $length, int $type = PHP_BINARY_READ)\n" "{\n" " if ($type != PHP_BINARY_READ) {\n" - " throw new RuntimeException('PHP_NORMAL_READ type is not supported');\n" + " return $socket->recvLine($length);\n" " }\n" " 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" @@ -7041,7 +9663,7 @@ static const char* swoole_library_source_ext_sockets = " return $socket->send($buffer);\n" "}\n" "\n" - "function swoole_socket_send(Socket $socket, string $buffer, int $length, int $flags): int\n" + "function swoole_socket_send(Socket $socket, string $buffer, int $length, int $flags)\n" "{\n" " if ($flags != 0) {\n" " throw new RuntimeException(\"\\$flags[{$flags}] is not supported\");\n" @@ -7052,7 +9674,7 @@ static const char* swoole_library_source_ext_sockets = "function swoole_socket_recv(Socket $socket, &$buffer, int $length, int $flags)\n" "{\n" " if ($flags & MSG_OOB) {\n" - " throw new RuntimeException('$flags[MSG_OOB] is not supported');\n" + " throw new RuntimeException('\\$flags[MSG_OOB] is not supported');\n" " }\n" " if ($flags & MSG_PEEK) {\n" " $buffer = $socket->peek($length);\n" @@ -7083,11 +9705,15 @@ static const char* swoole_library_source_ext_sockets = " return $socket->sendto($addr, $port, $buffer);\n" "}\n" "\n" - "function swoole_socket_recvfrom(Socket $socket, &$buffer, int $length, int $flags, &$name, &$port)\n" + "function swoole_socket_recvfrom(Socket $socket, &$buffer, int $length, int $flags, &$name, &$port = null)\n" "{\n" " if ($flags != 0) {\n" " throw new RuntimeException(\"\\$flags[{$flags}] is not supported\");\n" " }\n" + " if ($length == 0) {\n" + " $socket->errCode = SOCKET_EAGAIN;\n" + " return false;\n" + " }\n" " if ($socket->type != SOCK_DGRAM) {\n" " throw new RuntimeException('only supports dgram type socket');\n" " }\n" @@ -7104,7 +9730,7 @@ static const char* swoole_library_source_ext_sockets = " } else {\n" " $buffer = $data;\n" " }\n" - " return 100;\n" + " return strlen($buffer);\n" "}\n" "\n" "function swoole_socket_bind(Socket $socket, string $address, int $port = 0): bool\n" @@ -7180,9 +9806,9 @@ static const char* swoole_library_source_ext_sockets = " return $socket->getOption($level, $optname);\n" "}\n" "\n" - "function swoole_socket_shutdown(Socket $socket, int $how = 2)\n" + "function swoole_socket_shutdown(Socket $socket, int $how = 2): bool\n" "{\n" - " $socket->shutdown($how);\n" + " return $socket->shutdown($how);\n" "}\n" "\n" "function swoole_socket_close(Socket $socket)\n" @@ -7190,7 +9816,7 @@ static const char* swoole_library_source_ext_sockets = " $socket->close();\n" "}\n" "\n" - "function swoole_socket_clear_error(Socket $socket = null)\n" + "function swoole_socket_clear_error(?Socket $socket = null)\n" "{\n" " if ($socket) {\n" " $socket->errCode = 0;\n" @@ -7198,7 +9824,7 @@ static const char* swoole_library_source_ext_sockets = " swoole_clear_error();\n" "}\n" "\n" - "function swoole_socket_last_error(Socket $socket = null): int\n" + "function swoole_socket_last_error(?Socket $socket = null): int\n" "{\n" " if ($socket) {\n" " return $socket->errCode;\n" @@ -7208,22 +9834,50 @@ static const char* swoole_library_source_ext_sockets = "\n" "function swoole_socket_set_block(Socket $socket)\n" "{\n" + " if ($socket->isClosed()) {\n" + " return false;\n" + " }\n" " if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\n" - " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $socket->__ext_sockets_timeout);\n" + " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, $socket->__ext_sockets_timeout); // @phpstan-ignore property.notFound\n" " }\n" - " $socket->__ext_sockets_nonblock = false;\n" + " $socket->__ext_sockets_nonblock = false; // @phpstan-ignore property.notFound\n" " return true;\n" "}\n" "\n" "function swoole_socket_set_nonblock(Socket $socket)\n" "{\n" + " if ($socket->isClosed()) {\n" + " return false;\n" + " }\n" " if (isset($socket->__ext_sockets_nonblock) and $socket->__ext_sockets_nonblock) {\n" " return true;\n" " }\n" - " $socket->__ext_sockets_nonblock = true;\n" - " $socket->__ext_sockets_timeout = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO);\n" + " $socket->__ext_sockets_nonblock = true; // @phpstan-ignore property.notFound\n" + " $socket->__ext_sockets_timeout = $socket->getOption(SOL_SOCKET, SO_RCVTIMEO); // @phpstan-ignore property.notFound\n" " $socket->setOption(SOL_SOCKET, SO_RCVTIMEO, ['sec' => 0, 'usec' => 1000]);\n" " return true;\n" + "}\n" + "\n" + "function swoole_socket_create_pair(\n" + " int $domain,\n" + " int $type,\n" + " int $protocol,\n" + " array &$pair,\n" + ") {\n" + " $_pair = swoole_coroutine_socketpair($domain, $type, $protocol);\n" + " if ($_pair) {\n" + " $pair = $_pair;\n" + " return true;\n" + " }\n" + " return false;\n" + "}\n" + "\n" + "/**\n" + " * @since 5.0.0\n" + " */\n" + "function swoole_socket_import_stream(mixed $stream): Socket|false\n" + "{\n" + " return Socket::import($stream); // @phpstan-ignore staticMethod.notFound\n" "}\n"; static const char* swoole_library_source_functions = @@ -7238,11 +9892,11 @@ static const char* swoole_library_source_functions = "\n" "declare(strict_types=1);\n" "\n" - "if (PHP_VERSION_ID < 70200) {\n" - " throw new RuntimeException('require PHP version 7.2 or later');\n" + "if (PHP_VERSION_ID < 80100) { // @phpstan-ignore smaller.alwaysFalse\n" + " throw new RuntimeException('require PHP version 8.1 or later');\n" "}\n" "\n" - "if (SWOOLE_USE_SHORTNAME) {\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" " function _string(string $string = ''): Swoole\\StringObject\n" " {\n" " return new Swoole\\StringObject($string);\n" @@ -7259,6 +9913,37 @@ static const char* swoole_library_source_functions = " }\n" "}\n" "\n" + "class SwooleLibrary\n" + "{\n" + " /**\n" + " * @var array\n" + " */\n" + " public static array $options = [];\n" + "}\n" + "\n" + "/**\n" + " * @param array $options\n" + " */\n" + "function swoole_library_set_options(array $options): void\n" + "{\n" + " SwooleLibrary::$options = $options;\n" + "}\n" + "\n" + "function swoole_library_get_options(): array\n" + "{\n" + " return SwooleLibrary::$options;\n" + "}\n" + "\n" + "function swoole_library_set_option(string $key, mixed $value): void\n" + "{\n" + " SwooleLibrary::$options[$key] = $value;\n" + "}\n" + "\n" + "function swoole_library_get_option(string $key): mixed\n" + "{\n" + " return SwooleLibrary::$options[$key] ?? null;\n" + "}\n" + "\n" "function swoole_string(string $string = ''): Swoole\\StringObject\n" "{\n" " return new Swoole\\StringObject($string);\n" @@ -7281,33 +9966,32 @@ static const char* swoole_library_source_functions = " $table = new Swoole\\Table($size, 0.25);\n" "\n" " foreach ($_fields as $f) {\n" - " $_f = swoole_string($f)->trim()->split(':');\n" + " $_f = swoole_string($f)->trim()->split(':');\n" " $name = $_f->get(0)->trim()->toString();\n" " $type = $_f->get(1)->trim();\n" "\n" " switch ($type) {\n" - " case 'i':\n" - " case 'int':\n" - " $table->column($name, Swoole\\Table::TYPE_INT);\n" - " break;\n" - " case 'f':\n" - " case 'float':\n" - " $table->column($name, Swoole\\Table::TYPE_FLOAT);\n" - " break;\n" - " case 's':\n" - " case 'string':\n" - " if ($_f->count() < 3) {\n" - " throw new RuntimeException('need to give string length');\n" - " }\n" - " $length = intval($_f->get(2)->trim()->toString());\n" - " if ($length <= 0) {\n" - " throw new RuntimeException(\"invalid string length[{$length}]\");\n" - " }\n" - " $table->column($name, Swoole\\Table::TYPE_STRING, $length);\n" - " break;\n" - " default:\n" - " throw new RuntimeException(\"unknown field type[{$type}]\");\n" - " break;\n" + " case 'i':\n" + " case 'int':\n" + " $table->column($name, Swoole\\Table::TYPE_INT);\n" + " break;\n" + " case 'f':\n" + " case 'float':\n" + " $table->column($name, Swoole\\Table::TYPE_FLOAT);\n" + " break;\n" + " case 's':\n" + " case 'string':\n" + " if ($_f->count() < 3) {\n" + " throw new RuntimeException('need to give string length');\n" + " }\n" + " $length = (int) $_f->get(2)->trim()->toString();\n" + " if ($length <= 0) {\n" + " throw new RuntimeException(\"invalid string length[{$length}]\");\n" + " }\n" + " $table->column($name, Swoole\\Table::TYPE_STRING, $length);\n" + " break;\n" + " default:\n" + " throw new RuntimeException(\"unknown field type[{$type}]\");\n" " }\n" " }\n" "\n" @@ -7328,24 +10012,40 @@ static const char* swoole_library_source_functions = " return array_key_exists($key, $array) ? $array[$key] : $default_value;\n" "}\n" "\n" - "if (!function_exists('array_key_last')) {\n" - " function array_key_last(array $array)\n" - " {\n" - " if (!empty($array)) {\n" - " return key(array_slice($array, -1, 1, true));\n" - " }\n" - " return null;\n" - " }\n" + "function swoole_is_in_container()\n" + "{\n" + " $mountinfo = file_get_contents('/proc/self/mountinfo');\n" + " return strpos($mountinfo, 'kubepods') > 0 || strpos($mountinfo, 'docker') > 0;\n" "}\n" "\n" - "if (!function_exists('array_key_first')) {\n" - " function array_key_first(array $array)\n" - " {\n" - " foreach ($array as $key => $unused) {\n" - " return $key;\n" - " }\n" - " return null;\n" + "function swoole_container_cpu_num()\n" + "{\n" + " $swoole_cpu_num = intval(getenv('SWOOLE_CPU_NUM'));\n" + " if ($swoole_cpu_num > 0) {\n" + " return $swoole_cpu_num;\n" + " }\n" + " if (!swoole_is_in_container()) {\n" + " return swoole_cpu_num();\n" + " }\n" + " // cgroup v2\n" + " $cpu_max = '/sys/fs/cgroup/cpu.max';\n" + " if (file_exists($cpu_max)) {\n" + " $cpu_max = file_get_contents($cpu_max);\n" + " $fields = explode($cpu_max, ' ');\n" + " $quota_us = $fields[0];\n" + " if ($quota_us === 'max') { // @phpstan-ignore identical.alwaysFalse\n" + " return swoole_cpu_num();\n" + " }\n" + " $period_us = $fields[1] ?? 100000;\n" + " } else {\n" + " $quota_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us');\n" + " $period_us = file_get_contents('/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us');\n" " }\n" + " $cpu_num = floatval($quota_us) / floatval($period_us);\n" + " if ($cpu_num < 1) {\n" + " return swoole_cpu_num();\n" + " }\n" + " return intval(floor($cpu_num));\n" "}\n"; static const char* swoole_library_source_alias = @@ -7360,7 +10060,7 @@ static const char* swoole_library_source_alias = "\n" "declare(strict_types=1);\n" "\n" - "if (SWOOLE_USE_SHORTNAME) {\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" " class_alias(Swoole\\Coroutine\\WaitGroup::class, Co\\WaitGroup::class, true);\n" " class_alias(Swoole\\Coroutine\\Server::class, Co\\Server::class, true);\n" " class_alias(Swoole\\Coroutine\\Server\\Connection::class, Co\\Server\\Connection::class, true);\n" @@ -7383,88 +10083,97 @@ static const char* swoole_library_source_alias_ns = "\n" "declare(strict_types=1);\n" "\n" - "namespace Swoole\\Coroutine {\n" - " use Swoole\\Coroutine;\n" + "namespace Co;\n" + "\n" + "use Swoole\\Coroutine;\n" "\n" + "if (SWOOLE_USE_SHORTNAME) { // @phpstan-ignore if.alwaysTrue\n" " function run(callable $fn, ...$args)\n" " {\n" - " $s = new Scheduler();\n" - " $options = Coroutine::getOptions();\n" - " if (!isset($options['hook_flags'])) {\n" - " $s->set(['hook_flags' => SWOOLE_HOOK_ALL]);\n" - " }\n" - " $s->add($fn, ...$args);\n" - " return $s->start();\n" + " return \\Swoole\\Coroutine\\run($fn, ...$args);\n" " }\n" - "}\n" "\n" - "namespace Co {\n" - " if (SWOOLE_USE_SHORTNAME) {\n" - " function run(callable $fn, ...$args)\n" - " {\n" - " return \\Swoole\\Coroutine\\run($fn, ...$args);\n" - " }\n" + " function go(callable $fn, ...$args)\n" + " {\n" + " return Coroutine::create($fn, ...$args);\n" + " }\n" + "\n" + " function defer(callable $fn)\n" + " {\n" + " Coroutine::defer($fn);\n" " }\n" "}\n"; -static void php_swoole_load_library() +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_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 336c14a6564..00000000000 --- a/ext-src/php_swoole_mysql_proto.h +++ /dev/null @@ -1,1002 +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) - -#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=%" PRIu64 ", 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; - bool nul; - const char *body; - 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, 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 new file mode 100644 index 00000000000..049c898e8c9 --- /dev/null +++ b/ext-src/php_swoole_private.h @@ -0,0 +1,1087 @@ +/* + +----------------------------------------------------------------------+ + | 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_PRIVATE_H +#define PHP_SWOOLE_PRIVATE_H + +// C++ build format macros must defined earlier +#ifdef __cplusplus +#define __STDC_FORMAT_MACROS +#endif + +#include "php_swoole.h" + +#define SW_HAVE_COUNTABLE 1 + +#include "swoole_c_api.h" +#include "swoole_api.h" +#include "swoole_async.h" + +#ifdef SW_HAVE_ZLIB +#include +#endif + +BEGIN_EXTERN_C() +#include +#include +#include +#include +#include +#include +#include + +#define PHP_SWOOLE_VERSION SWOOLE_VERSION +#define PHP_SWOOLE_CLIENT_USE_POLL + +extern PHPAPI int php_array_merge(zend_array *dest, zend_array *src); + +#ifdef PHP_WIN32 +#define PHP_SWOOLE_API __declspec(dllexport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define PHP_SWOOLE_API __attribute__((visibility("default"))) +#else +#define PHP_SWOOLE_API +#endif + +#define SW_CHECK_RETURN(s) \ + if (s < 0) { \ + RETURN_FALSE; \ + } else { \ + RETURN_TRUE; \ + } + +#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); \ + 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, ...) \ + 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__) + +/** + * 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__) + +#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 +#error "Enable c-ares support, require c-ares library" +#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 < 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 +//-------------------------------------------------------- +enum php_swoole_client_callback_type { + SW_CLIENT_CB_onConnect = 1, + SW_CLIENT_CB_onReceive, + SW_CLIENT_CB_onClose, + SW_CLIENT_CB_onError, + SW_CLIENT_CB_onBufferFull, + SW_CLIENT_CB_onBufferEmpty, +#ifdef SW_USE_OPENSSL + SW_CLIENT_CB_onSSLReady, +#endif +}; +//--------------------------------------------------------- +#define SW_FLAG_KEEP (1u << 12) +#define SW_FLAG_ASYNC (1u << 10) +#define SW_FLAG_SYNC (1u << 11) +//--------------------------------------------------------- +enum php_swoole_fd_type { + PHP_SWOOLE_FD_STREAM_CLIENT = SW_FD_STREAM_CLIENT, + PHP_SWOOLE_FD_DGRAM_CLIENT, + PHP_SWOOLE_FD_MYSQL, + PHP_SWOOLE_FD_REDIS, + PHP_SWOOLE_FD_HTTPCLIENT, + PHP_SWOOLE_FD_PROCESS_STREAM, + PHP_SWOOLE_FD_MYSQL_CORO, + PHP_SWOOLE_FD_REDIS_CORO, + PHP_SWOOLE_FD_POSTGRESQL, + PHP_SWOOLE_FD_SOCKET, + PHP_SWOOLE_FD_CO_CURL, +}; +//--------------------------------------------------------- +enum php_swoole_req_status { + PHP_SWOOLE_RINIT_BEGIN, + PHP_SWOOLE_RINIT_END, + PHP_SWOOLE_CALL_USER_SHUTDOWNFUNC_BEGIN, + PHP_SWOOLE_RSHUTDOWN_BEGIN, + PHP_SWOOLE_RSHUTDOWN_END, +}; +//--------------------------------------------------------- +enum php_swoole_hook_type { + PHP_SWOOLE_HOOK_BEFORE_ENABLE_HOOK = SW_GLOBAL_HOOK_USER, + PHP_SWOOLE_HOOK_AFTER_ENABLE_HOOK, + PHP_SWOOLE_HOOK_BEFORE_REQUEST, + PHP_SWOOLE_HOOK_AFTER_RESPONSE, +}; +//--------------------------------------------------------- + +static sw_inline enum swSocketType php_swoole_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; +extern zend_object_handlers swoole_redis_server_handlers; +extern zend_class_entry *swoole_connection_iterator_ce; +extern zend_class_entry *swoole_process_ce; +extern zend_class_entry *swoole_http_server_ce; +extern zend_object_handlers swoole_http_server_handlers; +extern zend_class_entry *swoole_websocket_server_ce; +extern zend_class_entry *swoole_websocket_frame_ce; +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); +PHP_FUNCTION(swoole_set_process_name); +//--------------------------------------------------------- +// Coroutine API +//--------------------------------------------------------- +PHP_FUNCTION(swoole_coroutine_create); +PHP_FUNCTION(swoole_coroutine_exec); +PHP_FUNCTION(swoole_coroutine_gethostbyname); +PHP_FUNCTION(swoole_coroutine_defer); +PHP_FUNCTION(swoole_coroutine_socketpair); +PHP_FUNCTION(swoole_test_kernel_coroutine); // for tests +//--------------------------------------------------------- +// event +//--------------------------------------------------------- +PHP_FUNCTION(swoole_client_select); +//--------------------------------------------------------- +// async[coro] +//--------------------------------------------------------- +PHP_FUNCTION(swoole_async_set); +PHP_FUNCTION(swoole_async_dns_lookup_coro); +//--------------------------------------------------------- +// error +//--------------------------------------------------------- +#define SW_STRERROR_SYSTEM 0 +#define SW_STRERROR_GAI 1 +#define SW_STRERROR_DNS 2 +#define SW_STRERROR_SWOOLE 9 + +/** + * MINIT + * ============================================================== + */ +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); +void php_swoole_timer_minit(int module_number); +// coroutine +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_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); +void php_swoole_set_coroutine_option(zend_array *vht); +void php_swoole_set_aio_option(zend_array *vht); + +// shutdown +void php_swoole_register_shutdown_function(const char *function); +void php_swoole_register_shutdown_function_prepend(const char *function); + +// event +void php_swoole_event_init(); +void php_swoole_event_wait(); +void php_swoole_event_exit(); + +/** + * MSHUTDOWN + * ============================================================== + */ +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); +} + +static sw_inline size_t php_swoole_get_send_data(zval *zdata, char **str) { + convert_to_string(zdata); + *str = Z_STRVAL_P(zdata); + return Z_STRLEN_P(zdata); +} + +int php_swoole_convert_to_fd(zval *zsocket); +int php_swoole_convert_to_fd_ex(zval *zsocket, int *async); + +#ifdef SWOOLE_SOCKETS_SUPPORT +php_socket *php_swoole_convert_to_socket(int sock); +#endif + +#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 + +zend_bool php_swoole_signal_isset_handler(int signo); + +#if PHP_VERSION_ID < 80200 +#define zend_atomic_bool zend_bool +#define zend_atomic_bool_store(atomic, desired) (*atomic = desired) +#endif + +#define sw_zend7_object zend_object +#define SW_Z8_OBJ_P(zobj) Z_OBJ_P(zobj) + +typedef ssize_t php_stream_size_t; +typedef zend_string error_filename_t; + +//----------------------------------Zval API------------------------------------ + +// Deprecated: do not use it anymore +// do not use sw_copy_to_stack(return_value, foo); +#define sw_copy_to_stack(ptr, val) \ + do { \ + (val) = *(zval *) (ptr); \ + (ptr) = &(val); \ + } while (0) + +#define SW_ZVAL_SOCKET(return_value, result) ZVAL_OBJ(return_value, &result->std) +#define SW_Z_SOCKET_P(zsocket) Z_SOCKET_P(zsocket) + +#ifndef ZVAL_IS_BOOL +static sw_inline zend_bool ZVAL_IS_BOOL(zval *v) { + return Z_TYPE_P(v) == IS_TRUE || Z_TYPE_P(v) == IS_FALSE; +} +#endif + +#ifndef ZVAL_IS_TRUE +static sw_inline zend_bool ZVAL_IS_TRUE(zval *v) { + return Z_TYPE_P(v) == IS_TRUE; +} +#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; +} +#endif + +#ifndef ZVAL_IS_LONG +static sw_inline zend_bool ZVAL_IS_LONG(zval *v) { + return Z_TYPE_P(v) == IS_LONG; +} +#endif + +#ifndef ZVAL_IS_STRING +static sw_inline zend_bool ZVAL_IS_STRING(zval *v) { + return Z_TYPE_P(v) == IS_STRING; +} +#endif + +#ifndef Z_BVAL_P +static sw_inline zend_bool Z_BVAL_P(zval *v) { + return Z_TYPE_P(v) == IS_TRUE; +} +#endif + +#ifndef ZVAL_IS_ARRAY +static sw_inline zend_bool ZVAL_IS_ARRAY(zval *v) { + return Z_TYPE_P(v) == IS_ARRAY; +} +#endif + +#ifndef ZVAL_IS_OBJECT +static sw_inline zend_bool ZVAL_IS_OBJECT(zval *v) { + return Z_TYPE_P(v) == IS_OBJECT; +} +#endif + +static sw_inline zval *sw_malloc_zval() { + return (zval *) emalloc(sizeof(zval)); +} + +static sw_inline zval *sw_zval_dup(zval *val) { + zval *dup = sw_malloc_zval(); + memcpy(dup, val, sizeof(zval)); + return dup; +} + +static sw_inline void sw_zval_free(zval *val) { + zval_ptr_dtor(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) +#define SW_REGISTER_BOOL_CONSTANT(name, value) REGISTER_BOOL_CONSTANT(name, value, CONST_CS | CONST_PERSISTENT) +#define SW_REGISTER_LONG_CONSTANT(name, value) REGISTER_LONG_CONSTANT(name, value, CONST_CS | CONST_PERSISTENT) +#define SW_REGISTER_DOUBLE_CONSTANT(name, value) REGISTER_DOUBLE_CONSTANT(name, value, CONST_CS | CONST_PERSISTENT) +#define SW_REGISTER_STRING_CONSTANT(name, value) \ + REGISTER_STRING_CONSTANT(name, (char *) value, CONST_CS | CONST_PERSISTENT) +#define SW_REGISTER_STRINGL_CONSTANT(name, value) \ + REGISTER_STRINGL_CONSTANT(name, (char *) value, CONST_CS | CONST_PERSISTENT) + +//----------------------------------Number API----------------------------------- + +#define sw_php_math_round(value, places, mode) _php_math_round(value, places, mode) + +//----------------------------------String API----------------------------------- + +#define SW_PHP_OB_START(zoutput) \ + zval zoutput; \ + do { \ + php_output_start_user(NULL, 0, PHP_OUTPUT_HANDLER_STDFLAGS); +#define SW_PHP_OB_END() \ + php_output_get_contents(&zoutput); \ + php_output_discard(); \ + } \ + while (0) + +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 > swoole_pagesize() && alloc_len > real_len * 2) { + s = zend_string_realloc(s, real_len, 0); + } else { + ZSTR_LEN(s) = real_len; + } + } + ZSTR_VAL(s)[real_len] = '\0'; + return s; +} + +//----------------------------------Array API------------------------------------ + +#define php_swoole_array_length(zarray) zend_hash_num_elements(Z_ARRVAL_P(zarray)) +#define php_swoole_array_get_value(ht, str, v) ((v = zend_hash_str_find(ht, str, sizeof(str) - 1)) && !ZVAL_IS_NULL(v)) +#define php_swoole_array_get_value_ex(ht, str, v) ((v = zend_hash_str_find(ht, str, strlen(str))) && !ZVAL_IS_NULL(v)) + +static sw_inline int php_swoole_array_length_safe(zval *zarray) { + if (zarray && ZVAL_IS_ARRAY(zarray)) { + return php_swoole_array_length(zarray); + } else { + return 0; + } +} + +void php_swoole_sha1(const char *str, int _len, uchar *digest); +void php_swoole_sha256(const char *str, int _len, uchar *digest); + +#define SW_HASHTABLE_FOREACH_START(ht, _val) \ + ZEND_HASH_FOREACH_VAL(ht, _val); \ + { +#define SW_HASHTABLE_FOREACH_START2(ht, k, klen, ktype, _val) \ + zend_string *_foreach_key; \ + ZEND_HASH_FOREACH_STR_KEY_VAL(ht, _foreach_key, _val); \ + if (!_foreach_key) { \ + k = NULL; \ + klen = 0; \ + ktype = 0; \ + } else { \ + k = ZSTR_VAL(_foreach_key), klen = ZSTR_LEN(_foreach_key); \ + ktype = 1; \ + } \ + { +#define SW_HASHTABLE_FOREACH_END() \ + } \ + ZEND_HASH_FOREACH_END(); + +static sw_inline void add_assoc_ulong_safe_ex(zval *arg, const char *key, size_t key_len, zend_ulong value) { + if (sw_likely(value <= ZEND_LONG_MAX)) { + add_assoc_long_ex(arg, key, key_len, value); + } else { + char buf[MAX_LENGTH_OF_LONG + 1]; + size_t len = sw_snprintf(buf, sizeof(buf), ZEND_ULONG_FMT, value); + add_assoc_stringl_ex(arg, key, key_len, buf, len); + } +} + +static sw_inline void add_assoc_ulong_safe(zval *arg, const char *key, zend_ulong value) { + add_assoc_ulong_safe_ex(arg, key, strlen(key), value); +} + +//----------------------------------Class API------------------------------------ + +#define SW_Z_OBJCE_NAME_VAL_P(zobject) ZSTR_VAL(Z_OBJCE_P(zobject)->name) + +/* PHP 7 class declaration macros */ + +#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 (short_name) SW_CLASS_ALIAS_SHORT_NAME(short_name, module); \ + } while (0) + +#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, 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, 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); \ + memcpy(&module##_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)) + +#define SW_CLASS_ALIAS(name, module) \ + do { \ + if (name) { \ + sw_zend_register_class_alias(ZEND_STRL(name), module##_ce); \ + } \ + } while (0) + +#define SW_CLASS_ALIAS_SHORT_NAME(short_name, module) \ + do { \ + if (SWOOLE_G(use_shortname)) { \ + SW_CLASS_ALIAS(short_name, module); \ + } \ + } while (0) + +#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 + +#define SW_SET_CLASS_UNSET_PROPERTY_HANDLER(module, _unset_property) module##_handlers.unset_property = _unset_property + +#define SW_SET_CLASS_CREATE(module, _create_object) module##_ce->create_object = _create_object + +#define SW_SET_CLASS_DTOR(module, _dtor_obj) module##_handlers.dtor_obj = _dtor_obj + +#define SW_SET_CLASS_FREE(module, _free_obj) module##_handlers.free_obj = _free_obj + +#define SW_SET_CLASS_CREATE_AND_FREE(module, _create_object, _free_obj) \ + SW_SET_CLASS_CREATE(module, _create_object); \ + SW_SET_CLASS_FREE(module, _free_obj) + +#define SW_SET_CLASS_CUSTOM_OBJECT(module, _create_object, _free_obj, _struct, _std) \ + SW_SET_CLASS_CREATE_AND_FREE(module, _create_object, _free_obj); \ + module##_handlers.offset = XtOffsetOf(_struct, _std) + +#define SW_PREVENT_USER_DESTRUCT() \ + do { \ + if (sw_unlikely(!(GC_FLAGS(Z_OBJ_P(ZEND_THIS)) & IS_OBJ_DESTRUCTOR_CALLED))) { \ + RETURN_NULL(); \ + } \ + } while (0) + +#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, + 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); + zend_string_release(lowercase_origin); + if (UNEXPECTED(!origin_function)) { + return FAILURE; + } + 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, 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; +} + +static sw_inline int sw_zend_register_class_alias(const char *name, size_t name_len, zend_class_entry *ce) { + zend_string *_name; + if (name[0] == '\\') { + _name = zend_string_init(name, name_len, 1); + zend_str_tolower_copy(ZSTR_VAL(_name), name + 1, name_len - 1); + } else { + _name = zend_string_init(name, name_len, 1); + zend_str_tolower_copy(ZSTR_VAL(_name), name, name_len); + } + + zend_string *_interned_name = zend_new_interned_string(_name); + + return zend_register_class_alias_ex(ZSTR_VAL(_interned_name), ZSTR_LEN(_interned_name), ce, 1); +} + +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); + object_properties_init(object, ce); + object->handlers = handlers; + return object; +} + +static sw_inline zend_object *sw_zend_create_object_deny(zend_class_entry *ce) { + zend_object *object; + object = zend_objects_new(ce); + /* Initialize default properties */ + if (EXPECTED(ce->default_properties_count != 0)) { + zval *p = object->properties_table; + zval *end = p + ce->default_properties_count; + do { + ZVAL_UNDEF(p); + p++; + } while (p != end); + } + zend_throw_error(NULL, "The object of %s can not be created for security reasons", ZSTR_VAL(ce->name)); + return object; +} + +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) { + ce = ce->parent; + } + SW_ASSERT(ce->type == ZEND_INTERNAL_CLASS); + if (EXPECTED(zend_hash_find(&ce->properties_info, member))) { + zend_throw_error(NULL, "Property %s of class %s cannot be unset", ZSTR_VAL(member), ZSTR_VAL(object->ce->name)); + return; + } + std_object_handlers.unset_property(object, member, cache_slot); +} + +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); + return zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, silent, &rv); + } + return property; +} + +static sw_inline void sw_zend_update_property_null_ex(zend_class_entry *scope, zval *object, zend_string *s) { + zval tmp; + + ZVAL_NULL(&tmp); + 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 *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))) { + ZVAL_NULL(property); + } + return property; +} + +static sw_inline zval *sw_zend_read_property_not_null( + 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; +} + +static sw_inline zval *sw_zend_read_property_not_null_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); + zend_uchar type = Z_TYPE_P(property); + 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, + size_t len) { + zval ztmp; + array_init(&ztmp); + zend_update_property(ce, SW_Z8_OBJ_P(obj), s, len, &ztmp); + zval_ptr_dtor(&ztmp); + return zend_read_property(ce, SW_Z8_OBJ_P(obj), s, len, 1, &ztmp); +} + +static sw_inline zval *sw_zend_read_and_convert_property_array( + 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 + if (UNEXPECTED(property == &EG(uninitialized_zval))) { + property = sw_zend_update_and_read_property_array(ce, obj, s, len); + } else { + zval_ptr_dtor(property); + array_init(property); + } + } + + return property; +} + +#define SW_RETURN_PROPERTY(name) \ + do { \ + RETURN_ZVAL(sw_zend_read_property(Z_OBJCE_P(ZEND_THIS), ZEND_THIS, ZEND_STRL(name), 0), 1, 0); \ + } while (0) + +#define RETURN_SW_STRING(buf) \ + do { \ + RETURN_STRINGL(buf->str, buf->length); \ + } while (0) + +//----------------------------------Function API------------------------------------ + +/** + * Notice (sw_zend_call_method_with_%u_params): If you don't want to check the return value, please set retval to NULL + */ +#define sw_zend_call_method_with_0_params(zobj, obj_ce, fn_ptr_ptr, fn_name, retval) \ + zend_call_method_with_0_params(SW_Z8_OBJ_P(zobj), obj_ce, fn_ptr_ptr, fn_name, retval) + +#define sw_zend_call_method_with_1_params(zobj, obj_ce, fn_ptr_ptr, fn_name, retval, v1) \ + zend_call_method_with_1_params(SW_Z8_OBJ_P(zobj), obj_ce, fn_ptr_ptr, fn_name, retval, v1) + +#define sw_zend_call_method_with_2_params(zobj, obj_ce, fn_ptr_ptr, fn_name, retval, v1, v2) \ + zend_call_method_with_2_params(SW_Z8_OBJ_P(zobj), obj_ce, fn_ptr_ptr, fn_name, retval, v1, v2) + +static sw_inline int sw_zend_function_max_num_args(zend_function *function) { + // https://github.com/php/php-src/commit/2646f7bcb98dcdd322ea21701c8bb101104ea619 + // zend_function.common.num_args don't include the variadic argument anymore. + return (function->common.fn_flags & ZEND_ACC_VARIADIC) ? UINT32_MAX : function->common.num_args; +} + +// TODO: remove it after remove async modules +static sw_inline zend_bool sw_zend_is_callable(zval *callable, int check_flags, char **callable_name) { + zend_string *name; + zend_bool ret = zend_is_callable(callable, check_flags, &name); + *callable_name = estrndup(ZSTR_VAL(name), ZSTR_LEN(name)); + zend_string_release(name); + return ret; +} + +static sw_inline zend_bool sw_zend_is_callable_at_frame(zval *zcallable, + zval *zobject, + zend_execute_data *frame, + uint check_flags, + char **callable_name, + size_t *callable_name_len, + zend_fcall_info_cache *fci_cache, + char **error) { + 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)); + } + if (callable_name_len) { + *callable_name_len = ZSTR_LEN(name); + } + zend_string_release(name); + return ret; +} + +static sw_inline zend_bool sw_zend_is_callable_ex(zval *zcallable, + zval *zobject, + uint check_flags, + char **callable_name, + size_t *callable_name_len, + zend_fcall_info_cache *fci_cache, + char **error) { + return sw_zend_is_callable_at_frame( + zcallable, zobject, NULL, check_flags, callable_name, callable_name_len, fci_cache, error); +} + +/* this API can work well when retval is NULL */ +static sw_inline int sw_zend_call_function_ex( + zval *function_name, zend_fcall_info_cache *fci_cache, uint32_t param_count, zval *params, zval *retval) { + zend_fcall_info fci; + zval _retval; + int ret; + + fci.size = sizeof(fci); + fci.object = NULL; + if (!fci_cache || !fci_cache->function_handler) { + if (!function_name) { + php_swoole_fatal_error(E_WARNING, "Bad function"); + return FAILURE; + } + ZVAL_COPY_VALUE(&fci.function_name, function_name); + } else { + ZVAL_UNDEF(&fci.function_name); + } + fci.retval = retval ? retval : &_retval; + fci.param_count = param_count; + fci.params = params; + fci.named_params = NULL; + + ret = zend_call_function(&fci, fci_cache); + + if (!retval) { + zval_ptr_dtor(&_retval); + } + return ret; +} + +/* we must check for exception immediately if we don't have chances to go back to ZendVM (e.g event loop) */ +static sw_inline int sw_zend_call_function_ex2( + zval *function_name, zend_fcall_info_cache *fci_cache, uint32_t param_count, zval *params, zval *retval) { + int ret = sw_zend_call_function_ex(function_name, fci_cache, param_count, params, retval); + if (UNEXPECTED(EG(exception))) { + zend_exception_error(EG(exception), E_ERROR); + } + return ret; +} + +static sw_inline int sw_zend_call_function_anyway(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) { + zval retval; + if (!fci->retval) { + fci->retval = &retval; + } + int ret = zend_call_function(fci, fci_cache); + if (fci->retval == &retval) { + zval_ptr_dtor(&retval); + } + return ret; +} + +static sw_inline void sw_zend_fci_params_persist(zend_fcall_info *fci) { + if (fci->param_count > 0) { + uint32_t i; + zval *params = (zval *) ecalloc(fci->param_count, sizeof(zval)); + for (i = 0; i < fci->param_count; i++) { + ZVAL_COPY(¶ms[i], &fci->params[i]); + } + fci->params = params; + } +} + +static sw_inline void sw_zend_fci_params_discard(zend_fcall_info *fci) { + if (fci->param_count > 0) { + uint32_t i; + for (i = 0; i < fci->param_count; i++) { + zval_ptr_dtor(&fci->params[i]); + } + efree(fci->params); + } +} + +static sw_inline void sw_zend_fci_cache_persist(zend_fcall_info_cache *fci_cache) { + if (fci_cache->object) { + GC_ADDREF(fci_cache->object); + } + if (fci_cache->function_handler->op_array.fn_flags & ZEND_ACC_CLOSURE) { + GC_ADDREF(ZEND_CLOSURE_OBJECT(fci_cache->function_handler)); + } +} + +static sw_inline void sw_zend_fci_cache_discard(zend_fcall_info_cache *fci_cache) { + if (fci_cache->object) { + OBJ_RELEASE(fci_cache->object); + } + if (fci_cache->function_handler->op_array.fn_flags & ZEND_ACC_CLOSURE) { + OBJ_RELEASE(ZEND_CLOSURE_OBJECT(fci_cache->function_handler)); + } +} + +#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------------------------------------ + +static sw_inline int php_swoole_check_reactor() { + if (SWOOLE_G(req_status) == PHP_SWOOLE_RSHUTDOWN_BEGIN) { + return -1; + } + if (sw_unlikely(!sw_reactor())) { + return php_swoole_reactor_init() == SW_OK ? 1 : -1; + } else { + return 0; + } +} + +static sw_inline char *php_swoole_format_date(char *format, size_t format_len, time_t ts, int localtime) { + zend_string *time = php_format_date(format, format_len, ts, localtime); + char *return_str = estrndup(ZSTR_VAL(time), ZSTR_LEN(time)); + zend_string_release(time); + return return_str; +} + +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)); + zend_string_release(str); + return return_str; +} + +static sw_inline char *php_swoole_http_build_query(zval *zdata, size_t *length, smart_str *formstr) { + 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 + 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); + } + return NULL; + } + if (!formstr->s) { + return NULL; + } + smart_str_0(formstr); + *length = formstr->s->len; + 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 fc00293000a..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 | +----------------------------------------------------------------------+ */ @@ -22,3 +22,7 @@ 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 32b49f72652..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 | +----------------------------------------------------------------------+ */ @@ -21,63 +21,141 @@ #include "php_swoole_cxx.h" #include "swoole_server.h" +#include +#include +#include + //-------------------------------------------------------- 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, // worker(event) - SW_SERVER_CB_onReceive, // worker(event) - SW_SERVER_CB_onClose, // worker(event) - SW_SERVER_CB_onPacket, // worker(event) - SW_SERVER_CB_onRequest, // http server - SW_SERVER_CB_onHandShake, // worker(event) - SW_SERVER_CB_onOpen, // worker(event) - SW_SERVER_CB_onMessage, // 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) -struct php_swoole_server_port_property { - 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]; - swServer *serv; - swListenPort *port; +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 { + zend::Callable *callbacks[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM]; + Server *serv; + ListenPort *port; zval *zsetting; }; +struct ServerProperty { + std::vector ports; + std::vector user_processes; + zend::Callable *callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM]; + std::unordered_map task_callbacks; + std::unordered_map task_coroutine_map; + std::unordered_map *> send_coroutine_map; + std::vector command_callbacks; +}; + +struct ServerObject { + Server *serv; + ServerProperty *property; + zval init_arguments; + zend_object std; + + zend_class_entry *get_ce() { + return Z_OBJCE_P(php_swoole_server_zval_ptr(serv)); + } + + 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(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() { + return instanceof_function(get_ce(), swoole_websocket_server_ce); + } + + zend_bool is_http_server() { + return instanceof_function(get_ce(), swoole_http_server_ce); + } + + zend_bool is_redis_server() { + return instanceof_function(get_ce(), swoole_redis_server_ce); + } + + void register_callback(); + void on_before_start(); + void copy_setting(zval *zsetting); +}; + +struct TaskCo { + Coroutine *co; + TaskId *list; + uint32_t count; + zval *result; +}; +void register_admin_server_commands(Server *serv); +} // namespace swoole + void php_swoole_server_register_callbacks(swServer *serv); -zend_fcall_info_cache *php_swoole_server_get_fci_cache(swServer *serv, int server_fd, int event_type); +zend::Callable *php_swoole_server_get_callback(swServer *serv, int server_fd, int event_type); int php_swoole_create_dir(const char *path, size_t length); void php_swoole_server_before_start(swServer *serv, zval *zobject); -void php_swoole_http_server_init_global_variant(); +bool php_swoole_server_isset_callback(swServer *serv, swListenPort *port, int event_type); 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_onConnect(swServer *, swDataHead *); -int php_swoole_onReceive(swServer *, swRecvData *); -int php_swoole_http_onReceive(swServer *, swRecvData *); -void php_swoole_http_onClose(swServer *, swDataHead *); -int php_swoole_onPacket(swServer *, swRecvData *); -void php_swoole_onClose(swServer *, swDataHead *); -void php_swoole_onBufferFull(swServer *, swDataHead *); -void php_swoole_onBufferEmpty(swServer *, swDataHead *); +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 *); +void php_swoole_server_onBufferFull(swServer *, swDataHead *); +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 2615d7e4b46..896728b4cde 100644 --- a/ext-src/swoole_async_coro.cc +++ b/ext-src/swoole_async_coro.cc @@ -10,16 +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 "php_swoole_cxx.h" -#include "php_streams.h" -#include "php_network.h" - -#include "ext/standard/file.h" -#include "ext/standard/basic_functions.h" +#include "swoole_socket.h" #include #include @@ -33,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++) { @@ -48,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; } @@ -115,18 +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; + zend_long type = AF_INET; double timeout = swoole::network::Socket::default_dns_timeout; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|d", &domain, &timeout) == 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"); @@ -151,7 +168,7 @@ PHP_FUNCTION(swoole_async_dns_lookup_coro) { php_swoole_check_reactor(); - vector result = swoole::coroutine::dns_lookup(Z_STRVAL_P(domain), timeout); + vector result = swoole::coroutine::dns_lookup(Z_STRVAL_P(domain), type, timeout); if (result.empty()) { swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); RETURN_FALSE; @@ -172,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 248661f6d14..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.h" +#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 707f87f32b4..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,42 +47,20 @@ 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 -enum swChannelErrorCode { - SW_CHANNEL_OK = 0, - SW_CHANNEL_TIMEOUT = -1, - SW_CHANNEL_CLOSED = -2, -}; - static sw_inline ChannelObject *php_swoole_channel_coro_fetch_object(zend_object *obj) { return (ChannelObject *) ((char *) obj - swoole_channel_coro_handlers.offset); } @@ -86,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; } @@ -124,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, @@ -142,9 +123,10 @@ void php_swoole_channel_coro_minit(int module_number) { zend_declare_property_long(swoole_channel_coro_ce, ZEND_STRL("capacity"), 0, ZEND_ACC_PUBLIC); zend_declare_property_long(swoole_channel_coro_ce, ZEND_STRL("errCode"), 0, ZEND_ACC_PUBLIC); - SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_OK", SW_CHANNEL_OK); - SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_TIMEOUT", SW_CHANNEL_TIMEOUT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_CLOSED", SW_CHANNEL_CLOSED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_OK", Channel::ERROR_OK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_TIMEOUT", Channel::ERROR_TIMEOUT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_CLOSED", Channel::ERROR_CLOSED); + SW_REGISTER_LONG_CONSTANT("SWOOLE_CHANNEL_CANCELED", Channel::ERROR_CANCELED); } static PHP_METHOD(swoole_channel_coro, __construct) { @@ -178,13 +160,12 @@ static PHP_METHOD(swoole_channel_coro, push) { Z_TRY_ADDREF_P(zdata); zdata = sw_zval_dup(zdata); if (chan->push(zdata, timeout)) { - zend_update_property_long(swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), SW_CHANNEL_OK); + zend_update_property_long( + swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), Channel::ERROR_OK); RETURN_TRUE; } else { - zend_update_property_long(swoole_channel_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - chan->is_closed() ? SW_CHANNEL_CLOSED : SW_CHANNEL_TIMEOUT); + zend_update_property_long( + swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), chan->get_error()); Z_TRY_DELREF_P(zdata); efree(zdata); RETURN_FALSE; @@ -204,12 +185,11 @@ static PHP_METHOD(swoole_channel_coro, pop) { if (zdata) { RETVAL_ZVAL(zdata, 0, 0); efree(zdata); - zend_update_property_long(swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), SW_CHANNEL_OK); + zend_update_property_long( + swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), Channel::ERROR_OK); } else { - zend_update_property_long(swoole_channel_coro_ce, - SW_Z8_OBJ_P(ZEND_THIS), - ZEND_STRL("errCode"), - chan->is_closed() ? SW_CHANNEL_CLOSED : SW_CHANNEL_TIMEOUT); + zend_update_property_long( + swoole_channel_coro_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), chan->get_error()); RETURN_FALSE; } } @@ -241,4 +221,3 @@ static PHP_METHOD(swoole_channel_coro, stats) { add_assoc_long_ex(return_value, ZEND_STRL("producer_num"), chan->producer_num()); add_assoc_long_ex(return_value, ZEND_STRL("queue_num"), chan->length()); } - diff --git a/ext-src/swoole_client.cc b/ext-src/swoole_client.cc index 2648d26807e..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,85 +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; - -#include "ext/standard/basic_functions.h" - -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; } @@ -137,111 +108,66 @@ 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 && cli->active == 1) { - return cli; - } else { - 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()); - php_swoole_error(E_WARNING, "client is not connected to server"); - return nullptr; + if (cli && cli->socket) { + if (cli->active) { + return cli; + } + if (cli->async_connect) { + cli->async_connect = false; + int error = -1; + if (cli->get_socket()->get_option(SOL_SOCKET, SO_ERROR, &error) == 0) { + if (error == 0) { + cli->active = 1; + return cli; + } + } + php_swoole_client_free(zobject, cli); + } } + 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()); + 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); @@ -314,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; @@ -356,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 @@ -389,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; @@ -423,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; @@ -432,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; } @@ -478,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); } } } @@ -558,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) { @@ -584,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); @@ -621,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); @@ -660,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); @@ -695,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", @@ -719,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; } @@ -736,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) { @@ -809,16 +710,19 @@ static PHP_METHOD(swoole_client, connect) { } } - // nonblock async if (cli->connect(cli, host, port, timeout, sock_flag) < 0) { - 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()); zend_update_property_long( swoole_client_ce, SW_Z8_OBJ_P(ZEND_THIS), ZEND_STRL("errCode"), swoole_get_last_error()); + // async connect + if (cli->async_connect) { + RETURN_TRUE; + } + 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; } @@ -841,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; } @@ -885,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 @@ -939,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; } @@ -982,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; } @@ -994,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; @@ -1012,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()); @@ -1043,7 +946,7 @@ static PHP_METHOD(swoole_client, recv) { buffer->length = 0; } - sw_set_zend_string(return_value, buffer->str, eof); + zend::assign_zend_string_by_val(return_value, buffer->str, eof); buffer->str = nullptr; delete buffer; @@ -1075,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; @@ -1085,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) { @@ -1137,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()); @@ -1171,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; } @@ -1197,17 +1103,22 @@ static PHP_METHOD(swoole_client, getsockname) { } } else { add_assoc_long(return_value, "port", ntohs(cli->socket->info.addr.inet_v4.sin_port)); - add_assoc_string(return_value, "host", inet_ntoa(cli->socket->info.addr.inet_v4.sin_addr)); + char tmp[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &cli->socket->info.addr.inet_v4.sin_addr, tmp, sizeof(tmp))) { + add_assoc_string(return_value, "host", tmp); + } else { + php_swoole_fatal_error(E_WARNING, "inet_ntop() failed"); + } } } #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; } @@ -1222,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; } @@ -1235,7 +1146,13 @@ static PHP_METHOD(swoole_client, getpeername) { if (cli->socket->socket_type == SW_SOCK_UDP) { array_init(return_value); add_assoc_long(return_value, "port", ntohs(cli->remote_addr.addr.inet_v4.sin_port)); - add_assoc_string(return_value, "host", inet_ntoa(cli->remote_addr.addr.inet_v4.sin_addr)); + char tmp[INET_ADDRSTRLEN]; + + if (inet_ntop(AF_INET, &cli->remote_addr.addr.inet_v4.sin_addr, tmp, sizeof(tmp))) { + add_assoc_string(return_value, "host", tmp); + } else { + php_swoole_fatal_error(E_WARNING, "inet_ntop() failed"); + } } else if (cli->socket->socket_type == SW_SOCK_UDP6) { array_init(return_value); add_assoc_long(return_value, "port", ntohs(cli->remote_addr.addr.inet_v6.sin6_port)); @@ -1297,32 +1214,49 @@ static PHP_METHOD(swoole_client, close) { } #ifdef SW_USE_OPENSSL -static PHP_METHOD(swoole_client, enableSSL) { - Client *cli = client_get_ptr(ZEND_THIS); - if (!cli) { - RETURN_FALSE; - } +bool php_swoole_client_enable_ssl_encryption(Client *cli, zval *zobject) { if (cli->socket->socket_type != SW_SOCK_TCP && cli->socket->socket_type != SW_SOCK_TCP6) { php_swoole_fatal_error(E_WARNING, "cannot use enableSSL"); - RETURN_FALSE; + return false; } if (cli->socket->ssl) { php_swoole_fatal_error(E_WARNING, "SSL has been enabled"); - RETURN_FALSE; + return false; } - cli->enable_ssl_encrypt(); - zval *zset = sw_zend_read_property_ex(swoole_client_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); + cli->open_ssl = 1; + zval *zset = sw_zend_read_property_ex(swoole_client_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); if (ZVAL_IS_ARRAY(zset)) { php_swoole_client_check_ssl_setting(cli, zset); } - if (cli->ssl_handshake() < 0) { + return cli->enable_ssl_encrypt() == SW_OK; +} + +static PHP_METHOD(swoole_client, enableSSL) { + zval *zcallback = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(zcallback) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (zcallback) { + zend_throw_exception( + swoole_exception_ce, "sync client does not support `onSslReady` callback", SW_ERROR_INVALID_PARAMS); RETURN_FALSE; } - RETURN_TRUE; + + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); + if (!cli) { + RETURN_FALSE; + } + if (!php_swoole_client_enable_ssl_encryption(cli, ZEND_THIS)) { + RETURN_FALSE; + } + RETURN_BOOL(cli->ssl_handshake() == SW_OK); } static PHP_METHOD(swoole_client, getPeerCert) { - Client *cli = client_get_ptr(ZEND_THIS); + Client *cli = php_swoole_client_get_cli_safe(ZEND_THIS); if (!cli) { RETURN_FALSE; } @@ -1337,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; } @@ -1354,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; } @@ -1372,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 25e10164fa0..4db4778e1ff 100644 --- a/ext-src/swoole_client_coro.cc +++ b/ext-src/swoole_client_coro.cc @@ -10,21 +10,23 @@ | to obtain it through the world-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 "swoole_string.h" +#include "swoole_socket.h" +#include "swoole_protocol.h" #include "swoole_proxy.h" -#include "swoole_mqtt.h" -#include "ext/standard/basic_functions.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; - } - } - - return ret; -} - -#ifdef SW_USE_OPENSSL -bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { - HashTable *vht = Z_ARRVAL_P(zset); - zval *ztmp; - bool ret = true; - - if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) { - zend_long v = zval_get_long(ztmp); - sock->get_ssl_context()->protocols = v; - } - if (php_swoole_array_get_value(vht, "ssl_compress", ztmp)) { - sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); - } else if (php_swoole_array_get_value(vht, "ssl_disable_compression", ztmp)) { - sock->get_ssl_context()->disable_compress = !zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_cert_file", ztmp)) { - zend::String str_v(ztmp); - if (access(str_v.val(), R_OK) == 0) { - sock->get_ssl_context()->cert_file = str_v.to_std_string(); - } else { - php_swoole_fatal_error(E_WARNING, "ssl cert file[%s] not found", str_v.val()); - ret = false; - } - } - if (php_swoole_array_get_value(vht, "ssl_key_file", ztmp)) { - zend::String str_v(ztmp); - if (access(str_v.val(), R_OK) == 0) { - sock->get_ssl_context()->key_file = str_v.to_std_string(); - } else { - php_swoole_fatal_error(E_WARNING, "ssl key file[%s] not found", str_v.val()); - ret = false; - } - } - if (!sock->get_ssl_context()->cert_file.empty() && sock->get_ssl_context()->key_file.empty()) { - php_swoole_fatal_error(E_WARNING, "ssl require key file"); - } else if (!sock->get_ssl_context()->key_file.empty() && sock->get_ssl_context()->cert_file.empty()) { - php_swoole_fatal_error(E_WARNING, "ssl require cert file"); - } - if (php_swoole_array_get_value(vht, "ssl_passphrase", ztmp)) { - sock->get_ssl_context()->passphrase = zend::String(ztmp).to_std_string(); - } -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (php_swoole_array_get_value(vht, "ssl_host_name", ztmp)) { - sock->get_ssl_context()->tls_host_name = zend::String(ztmp).to_std_string(); - /* if user set empty ssl_host_name, disable it, otherwise the underlying may set it automatically */ - sock->get_ssl_context()->disable_tls_host_name = sock->get_ssl_context()->tls_host_name.empty(); - } -#endif - if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) { - sock->get_ssl_context()->verify_peer = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_allow_self_signed", ztmp)) { - sock->get_ssl_context()->allow_self_signed = zval_is_true(ztmp); - } - if (php_swoole_array_get_value(vht, "ssl_cafile", ztmp)) { - sock->get_ssl_context()->cafile = zend::String(ztmp).to_std_string(); - } - if (php_swoole_array_get_value(vht, "ssl_capath", ztmp)) { - sock->get_ssl_context()->capath = zend::String(ztmp).to_std_string(); - } - if (php_swoole_array_get_value(vht, "ssl_verify_depth", ztmp)) { - zend_long v = zval_get_long(ztmp); - sock->get_ssl_context()->verify_depth = SW_MAX(0, SW_MIN(v, UINT8_MAX)); - } - if (!sock->ssl_check_context()) { - ret = false; + client->socket = sock; + zval *zset = sw_zend_read_property_ex(swoole_client_coro_ce, zobject, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); + if (zset && ZVAL_IS_ARRAY(zset)) { + php_swoole_socket_set(sock, zset); } - return ret; + return sock; } -#endif static PHP_METHOD(swoole_client_coro, __construct) { - if (php_swoole_get_client(ZEND_THIS)->sock) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); - } - zend_long type = 0; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 1) Z_PARAM_LONG(type) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - int client_type = php_swoole_socktype(type); - if (client_type < SW_SOCK_TCP || client_type > SW_SOCK_UNIX_DGRAM) { + enum swSocketType socket_type = (enum swSocketType) php_swoole_get_socket_type(type); + if (socket_type < SW_SOCK_TCP || socket_type > SW_SOCK_UNIX_DGRAM) { const char *space, *class_name = get_active_class_name(&space); zend_type_error("%s%s%s() expects parameter %d to be client type, unknown type " ZEND_LONG_FMT " given", class_name, @@ -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; @@ -734,7 +469,7 @@ static PHP_METHOD(swoole_client_coro, recv) { retval = -1; cli->set_err(ENOMEM); } else { - result = sw_get_zend_string(strval); + result = zend::fetch_zend_string_by_val(strval); } } } else { @@ -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,8 +518,8 @@ static PHP_METHOD(swoole_client_coro, peek) { } static PHP_METHOD(swoole_client_coro, isConnected) { - Socket *cli = php_swoole_get_sock(ZEND_THIS); - if (cli && cli->is_connect()) { + Socket *cli = client_coro_get_socket(ZEND_THIS); + if (cli && cli->is_connected()) { RETURN_TRUE; } else { RETURN_FALSE; @@ -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,37 +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; } 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())) { @@ -901,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 ce1cfc39ead..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,45 +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::active = 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 - static void (*orig_interrupt_function)(zend_execute_data *execute_data) = nullptr; -static void (*orig_error_function)(int type, - const char *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; @@ -86,6 +94,9 @@ 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); static PHP_METHOD(swoole_coroutine, getCid); static PHP_METHOD(swoole_coroutine, getPcid); @@ -93,104 +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_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_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 @@ -201,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; @@ -217,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) { @@ -249,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(); @@ -266,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(); } } @@ -282,38 +299,102 @@ 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 -void PHPCoroutine::deactivate(void *ptr) { - interrupt_thread_stop(); - /** - * reset runtime hook - */ - disable_hook(); + 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_interrupt_function = orig_interrupt_function; - zend_error_cb = orig_error_function; + 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)); - if (config.enable_deadlock_check) { - deadlock_check(); + 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; +} - active = false; +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() { - if (sw_unlikely(active)) { + if (sw_unlikely(activated)) { 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(); } /* init reactor and register event wait */ @@ -323,21 +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, const char *error_filename, const uint32_t error_lineno, ZEND_ERROR_CB_LAST_ARG_D) { - if (sw_unlikely(type & E_FATAL_ERRORS)) { - if (active) { - /* update the last coroutine's info */ - save_task(get_context()); - } - swoole_event_free(); - } - 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(); @@ -349,27 +415,58 @@ void PHPCoroutine::activate() { disable_unsafe_function(); + /* 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(); /** - * deactivate when reactor free. + * reset runtime hook */ - SwooleTG.reactor->add_destroy_callback(deactivate, nullptr); - active = true; + disable_hook(); + + Coroutine::set_on_yield(nullptr); + Coroutine::set_on_resume(nullptr); + Coroutine::set_on_close(nullptr); + + zend_interrupt_function = orig_interrupt_function; + + if (config.enable_deadlock_check) { + deadlock_check(); + } + + enable_unsafe_function(); + Coroutine::deactivate(); } 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() { if (Coroutine::count() == 0) { return; } - if (php_swoole_is_fatal_error()) { + if (php_swoole_is_fatal_error() || (sw_reactor() && sw_reactor()->bailout)) { return; } if (SWOOLE_G(enable_library)) { @@ -397,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 * @@ -441,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; } } @@ -534,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) { @@ -590,182 +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); + + // Catch exception in main function of the coroutine + exception_caught = catch_exception(); - if (task->defer_tasks) { - std::stack *tasks = task->defer_tasks; + // The defer tasks still need to be executed after an exception occurs + if (ctx->defer_tasks) { + std::stack *tasks = ctx->defer_tasks; while (!tasks->empty()) { zend::Function *defer_fci = tasks->top(); tasks->pop(); - defer_fci->fci.param_count = 1; - defer_fci->fci.params = retval; + 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); - } - zval_ptr_dtor(retval); - - // TODO: exceptions will only cause the coroutine to exit - if (UNEXPECTED(EG(exception))) { - zend_exception_error(EG(exception), E_ERROR); + delete ctx->defer_tasks; + ctx->defer_tasks = nullptr; } - -#ifdef SW_CORO_SUPPORT_BAILOUT } zend_catch { - Coroutine::bailout([]() { - swoole_event_free(); - 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; @@ -780,7 +806,7 @@ long PHPCoroutine::create(zend_fcall_info_cache *fci_cache, uint32_t argc, zval return Coroutine::ERR_INVALID; } - if (sw_unlikely(!active)) { + if (sw_unlikely(!activated)) { activate(); } @@ -788,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); @@ -842,21 +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); @@ -864,9 +1044,17 @@ void php_swoole_coroutine_minit(int module_number) { 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(); } @@ -896,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 { @@ -921,13 +1109,21 @@ PHP_FUNCTION(swoole_coroutine_defer) { PHPCoroutine::defer(defer_fci); } -PHP_METHOD(swoole_coroutine, stats) { +static PHP_METHOD(swoole_coroutine, stats) { array_init(return_value); - add_assoc_long_ex(return_value, ZEND_STRL("event_num"), SwooleTG.reactor ? SwooleTG.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); - add_assoc_long_ex(return_value, ZEND_STRL("aio_task_num"), SwooleTG.aio_task_num); - add_assoc_long_ex(return_value, ZEND_STRL("aio_worker_num"), swoole::async::thread_count()); + + if (SwooleTG.async_threads) { + 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()); add_assoc_long_ex(return_value, ZEND_STRL("coroutine_peak_num"), Coroutine::get_peak_num()); @@ -955,7 +1151,7 @@ PHP_METHOD(swoole_coroutine, getPcid) { RETURN_LONG(ret); } -PHP_METHOD(swoole_coroutine, getContext) { +static PHP_METHOD(swoole_coroutine, getContext) { zend_long cid = 0; ZEND_PARSE_PARAMETERS_START(0, 1) @@ -963,25 +1159,26 @@ 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); } -PHP_METHOD(swoole_coroutine, getElapsed) { +static PHP_METHOD(swoole_coroutine, getElapsed) { zend_long cid = 0; zend_long ret; @@ -994,7 +1191,22 @@ PHP_METHOD(swoole_coroutine, getElapsed) { RETURN_LONG(ret); } -PHP_METHOD(swoole_coroutine, exists) { +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; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -1004,32 +1216,163 @@ PHP_METHOD(swoole_coroutine, exists) { RETURN_BOOL(Coroutine::get_by_cid(cid) != nullptr); } -PHP_METHOD(swoole_coroutine, resume) { - long cid; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &cid) == FAILURE) { - RETURN_FALSE; - } +static PHP_METHOD(swoole_coroutine, resume) { + 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; } Coroutine *co = coroutine_iterator->second; user_yield_coros.erase(cid); co->resume(); + RETURN_TRUE; } -PHP_METHOD(swoole_coroutine, yield) { +static PHP_METHOD(swoole_coroutine, yield) { Coroutine *co = Coroutine::get_current_safe(); user_yield_coros[co->get_cid()] = co; - co->yield(); + + Coroutine::CancelFunc cancel_fn = [](Coroutine *co) { + user_yield_coros.erase(co->get_cid()); + co->resume(); + return true; + }; + co->yield(&cancel_fn); + if (co->is_canceled()) { + swoole_set_last_error(SW_ERROR_CO_CANCELED); + RETURN_FALSE; + } + + RETURN_TRUE; +} + +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; } -PHP_METHOD(swoole_coroutine, getBackTrace) { +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); + RETURN_FALSE; + } + RETURN_BOOL(co->cancel()); +} + +static PHP_METHOD(swoole_coroutine, isCanceled) { + Coroutine *co = Coroutine::get_current_safe(); + RETURN_BOOL(co->is_canceled()); +} + +PHP_FUNCTION(swoole_test_kernel_coroutine) { + if (!PHPCoroutine::is_activated()) { + RETURN_FALSE; + } + + zend_long count = 100; + double sleep_time = 1.0; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(count) + Z_PARAM_DOUBLE(sleep_time) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + Coroutine::create([=](void *ptr) { + SW_LOOP_N(count) { + System::sleep(sleep_time); + } + }); +} + +static PHP_METHOD(swoole_coroutine, getBackTrace) { zend_long cid = 0; zend_long options = DEBUG_BACKTRACE_PROVIDE_OBJECT; zend_long limit = 0; @@ -1046,6 +1389,7 @@ PHP_METHOD(swoole_coroutine, getBackTrace) { } else { PHPContext *task = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); if (UNEXPECTED(!task)) { + swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); RETURN_FALSE; } zend_execute_data *ex_backup = EG(current_execute_data); @@ -1055,18 +1399,7 @@ PHP_METHOD(swoole_coroutine, getBackTrace) { } } -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); @@ -1074,18 +1407,37 @@ 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)) { - RETURN_FALSE; + PHPContext *ctx = (PHPContext *) PHPCoroutine::get_context_by_cid(cid); + if (UNEXPECTED(!ctx)) { + swoole_set_last_error(SW_ERROR_CO_NOT_EXISTS); + 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; } } -PHP_METHOD(swoole_coroutine, list) { +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); for (auto &co : Coroutine::coroutines) { @@ -1101,6 +1453,12 @@ 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 aa2195a12ea..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)) { - if (errno == EBUSY) { + 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 (errno == EINVAL) { - php_swoole_fatal_error(E_WARNING, "Invalid signal [" ZEND_LONG_FMT "]", signo); + } else if (swoole_get_last_error() == EINVAL) { + php_swoole_fatal_error(E_WARNING, "Invalid signal in the given list"); } - swoole_set_last_error(errno); + errno = swoole_get_last_error(); RETURN_FALSE; } - RETURN_TRUE; + RETURN_LONG(signo); } PHP_METHOD(swoole_coroutine_system, waitEvent) { @@ -671,11 +354,14 @@ PHP_METHOD(swoole_coroutine_system, waitEvent) { int fd = php_swoole_convert_to_fd(zfd); if (fd < 0) { - php_swoole_fatal_error(E_WARNING, "unknow fd type"); + php_swoole_fatal_error(E_WARNING, "unknown fd type"); RETURN_FALSE; } events = System::wait_event(fd, events, timeout); + if (events < 0) { + RETURN_FALSE; + } RETURN_LONG(events); } diff --git a/ext-src/swoole_curl.cc b/ext-src/swoole_curl.cc new file mode 100644 index 00000000000..85b8deefe59 --- /dev/null +++ b/ext-src/swoole_curl.cc @@ -0,0 +1,513 @@ +/* + +----------------------------------------------------------------------+ + | 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_curl.h" +#include "swoole_socket.h" + +#ifdef SW_USE_CURL + +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; + 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); +} + +int Multi::cb_writable(Reactor *reactor, Event *event) { + return execute_callback(event, CURL_CSELECT_OUT); +} + +int Multi::cb_error(Reactor *reactor, Event *event) { + return execute_callback(event, CURL_CSELECT_ERR); +} + +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, sockfd, action); + break; + case CURL_POLL_REMOVE: + if (socketp) { + multi->del_event(easy, socketp, sockfd); + } + break; + default: + abort(); + } + return 0; +} + +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); + } + + 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; +} + +void Multi::del_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd) { + 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_--; + } + curl_multi_assign(multi_handle_, sockfd, NULL); + + Handle *handle = get_handle(cp); + if (handle) { + handle->destroy_socket(sockfd); + } + + swoole_trace_log(SW_TRACE_CO_CURL, SW_ECHO_RED " handle=%p, curl=%p, fd=%d", "[DEL_EVENT]", handle, cp, sockfd); +} + +void Multi::set_event(CURL *cp, void *socket_ptr, curl_socket_t sockfd, int action) { + 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; + } + if (action != CURL_POLL_OUT) { + events |= SW_EVENT_READ; + } + 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(curl_socket->socket, events) == SW_OK) { + event_count_++; + } + } + + 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(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) { + 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(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(Handle *handle) { + if (add_handle(handle) != CURLM_OK) { + return CURLE_FAILED_INIT; + } + + 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); + 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) { + 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) { + 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) { + 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(handle); + return is_canceled ? CURLE_ABORTED_BY_CALLBACK : retval; +} + +CURLcode Multi::read_info() { + CURLMsg *message; + int pending; + + while ((message = curl_multi_info_read(multi_handle_, &pending))) { + switch (message->msg) { + case CURLMSG_DONE: + /* Do not use message data after calling curl_multi_remove_handle() and + curl_easy_cleanup(). As per curl_multi_info_read() docs: + "WARNING: The data the returned pointer points to will not survive + calling curl_multi_cleanup, curl_multi_remove_handle or + curl_easy_cleanup." */ + return message->data.result; + default: + swoole_warning("CURLMSG default"); + break; + } + } + return CURLE_OK; +} + +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 -1; + } + if (timeout_ms < 0) { + 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 */ + } + multi->add_timer(timeout_ms); + } + return 0; +} + +long Multi::select(php_curlm *mh, double timeout) { + if (zend_llist_count(&mh->easyh) == 0) { + return 0; + } + + 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; + php_curl *ch; + if ((ch = swoole_curl_get_handle(z_ch, false)) == NULL) { + continue; + } + Handle *handle = get_handle(ch->cp); + + 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()); + } + } + } + } + set_timer(); + + // no events and timers, should not be suspended + if (!timer && event_count_ == 0) { + 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(); + + for (zend_llist_element *element = mh->easyh.head; element; element = element->next) { + zval *z_ch = (zval *) element->data; + php_curl *ch; + if ((ch = swoole_curl_get_handle(z_ch, false)) == NULL) { + continue; + } + Handle *handle = get_handle(ch->cp); + 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_, 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; + 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(); + + return count; +} + +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 = sockfd; + } else { + last_sockfd = -1; + } + if (selector.get()) { + if (!handle) { + selector->timer_callback = true; + } + } + 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 (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 83b9aa9aecd..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"); @@ -260,28 +252,14 @@ int php_swoole_reactor_init() { } void php_swoole_event_wait() { - 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; - default: - break; - } - } - - if (!sw_reactor()) { + 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()) { + 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 #if defined(EG_FLAGS_IN_SHUTDOWN) && !defined(EG_FLAGS_OBJECT_STORE_NO_REUSE) @@ -320,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; } @@ -342,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; @@ -392,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 @@ -447,28 +398,23 @@ 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) { - php_swoole_fatal_error(E_WARNING, "unknow fd type"); + php_swoole_fatal_error(E_WARNING, "unknown fd type"); RETURN_FALSE; } if (socket_fd == 0 && (events & SW_EVENT_WRITE)) { @@ -483,34 +429,41 @@ 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; if (swoole_event_add(socket, events) < 0) { php_swoole_fatal_error(E_WARNING, "swoole_event_add failed"); - efree(socket); + socket->free(); event_object_free(peo); RETURN_FALSE; } @@ -525,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"); @@ -536,7 +490,7 @@ static PHP_FUNCTION(swoole_event_write) { int socket_fd = php_swoole_convert_to_fd(zfd); if (socket_fd < 0) { - php_swoole_fatal_error(E_WARNING, "unknow type"); + php_swoole_fatal_error(E_WARNING, "unknown type"); RETURN_FALSE; } @@ -561,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, "unknow type"); RETURN_FALSE; } @@ -587,28 +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); } - 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); } - 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) { @@ -627,13 +579,13 @@ 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) { - php_swoole_fatal_error(E_WARNING, "unknow type"); + php_swoole_fatal_error(E_WARNING, "unknown type"); RETURN_FALSE; } @@ -650,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(); } @@ -732,11 +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 (!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_fatal_error(E_DEPRECATED, "Event::wait() in shutdown function is deprecated"); - php_swoole_event_wait(); } zend_end_try(); } @@ -746,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; } @@ -775,7 +711,7 @@ static PHP_FUNCTION(swoole_event_isset) { int socket_fd = php_swoole_convert_to_fd(zfd); if (socket_fd < 0) { - php_swoole_fatal_error(E_WARNING, "unknow type"); + php_swoole_fatal_error(E_WARNING, "unknown type"); RETURN_FALSE; } diff --git a/ext-src/swoole_http2_client_coro.cc b/ext-src/swoole_http2_client_coro.cc index e605bf3c7ca..f59d8050a96 100644 --- a/ext-src/swoole_http2_client_coro.cc +++ b/ext-src/swoole_http2_client_coro.cc @@ -10,26 +10,31 @@ | to obtain it through the world-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_http.h" +#include "swoole_string.h" +#include "swoole_protocol.h" +#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_http.h" #include "swoole_http2.h" #define HTTP2_CLIENT_HOST_HEADER_INDEX 3 -#include - using namespace swoole; using swoole::coroutine::Socket; +using swoole::http2::get_default_setting; + +namespace Http2 = swoole::http2; static zend_class_entry *swoole_http2_client_coro_ce; static zend_object_handlers swoole_http2_client_coro_handlers; @@ -43,7 +48,9 @@ static zend_object_handlers swoole_http2_request_handlers; static zend_class_entry *swoole_http2_response_ce; static zend_object_handlers swoole_http2_response_handlers; -namespace swoole { namespace coroutine { namespace http2 { +namespace swoole { +namespace coroutine { +namespace http2 { struct Stream { uint32_t stream_id; @@ -68,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 - swHttp2_settings local_settings = {}; - swHttp2_settings remote_settings = {}; + 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); @@ -91,7 +100,8 @@ class Client { open_ssl = _ssl; _zobject = *__zobject; zobject = &_zobject; - swHttp2_init_settings(&local_settings); + Http2::init_settings(&local_settings); + local_window_size = local_settings.init_window_size; } inline Stream *get_stream(uint32_t stream_id) { @@ -106,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) { @@ -119,27 +128,21 @@ class Client { } inline bool is_available() { - if (sw_unlikely(!client)) { - 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"), ECONNRESET); - 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; } @@ -168,60 +171,91 @@ 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; } }; -}}} +} // namespace http2 +} // namespace coroutine +} // namespace swoole using swoole::coroutine::http2::Client; 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 void php_swoole_set_h2c(zval *zobject, Client *h2c) { - php_swoole_http2_client_coro_fetch_object(Z_OBJ_P(zobject))->h2c = h2c; +static sw_inline Client *http2_client_coro_get_client(zval *zobject) { + return http2_client_coro_fetch_object(Z_OBJ_P(zobject))->client; } -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); @@ -246,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); @@ -355,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); @@ -389,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 = swHttp2_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; @@ -430,7 +448,8 @@ bool Client::connect() { stream_id = 1; // [init]: we must set default value, server is not always send all the settings - swHttp2_init_settings(&remote_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) { @@ -455,45 +474,36 @@ 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; - ssize_t length = swHttp2_get_length(buf); + ssize_t length = Http2::get_length(buf); buf += SW_HTTP2_FRAME_HEADER_SIZE; char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; @@ -508,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) { @@ -526,44 +536,43 @@ 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); length -= sizeof(id) + sizeof(value); } - swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_SETTINGS, 0, SW_HTTP2_FLAG_ACK, stream_id); + Http2::set_frame_header(frame, SW_HTTP2_TYPE_SETTINGS, 0, SW_HTTP2_FLAG_ACK, stream_id); if (!send(frame, SW_HTTP2_FRAME_HEADER_SIZE)) { return SW_ERROR; } @@ -571,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) { @@ -583,9 +593,9 @@ 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)) { - swHttp2_set_frame_header( + Http2::set_frame_header( frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_ACK, stream_id); memcpy( frame + SW_HTTP2_FRAME_HEADER_SIZE, buf + SW_HTTP2_FRAME_HEADER_SIZE, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE); @@ -600,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); @@ -618,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); @@ -631,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); @@ -639,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) { @@ -662,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); @@ -674,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; } } } @@ -740,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(); @@ -753,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; } @@ -797,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); @@ -818,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) @@ -834,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); - swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_WINDOW_UPDATE, SW_HTTP2_WINDOW_UPDATE_SIZE, 0, stream_id); + 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() { - swHttp2_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)); - swHttp2_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]", swHttp2_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; @@ -918,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] == ':') { @@ -939,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 */ @@ -952,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); } @@ -975,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); @@ -1040,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) { @@ -1062,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"); @@ -1097,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); @@ -1108,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]; - swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0); + 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); } @@ -1119,14 +1121,14 @@ 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; send_flag = flag; } - swHttp2_set_frame_header(header, SW_HTTP2_TYPE_DATA, send_len, send_flag, stream_id); + Http2::set_frame_header(header, SW_HTTP2_TYPE_DATA, send_len, send_flag, stream_id); if (!send(header, SW_HTTP2_FRAME_HEADER_SIZE)) { return false; } @@ -1139,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); @@ -1180,13 +1185,10 @@ uint32_t Client::send_request(zval *zrequest) { flags |= SW_HTTP2_FLAG_END_STREAM; } - swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_HEADERS, bytes, flags, stream->stream_id); + 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", - swHttp2_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; } @@ -1213,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", - swHttp2_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; @@ -1233,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); @@ -1253,13 +1257,13 @@ bool Client::write_data(uint32_t stream_id, zval *zdata, bool end) { php_swoole_error(E_WARNING, "http_build_query failed"); return false; } - swHttp2_set_frame_header(buffer, SW_HTTP2_TYPE_DATA, len, flag, stream_id); - swTraceLog(SW_TRACE_HTTP2, - "[" SW_ECHO_GREEN ",%s STREAM#%d] length=%zu", - swHttp2_get_type(SW_HTTP2_TYPE_DATA), - end ? " END," : "", - stream_id, - len); + Http2::set_frame_header(buffer, SW_HTTP2_TYPE_DATA, len, flag, stream_id); + 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; @@ -1267,13 +1271,13 @@ bool Client::write_data(uint32_t stream_id, zval *zdata, bool end) { smart_str_free(&formstr_s); } else { zend::String data(zdata); - swHttp2_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", - swHttp2_get_type(SW_HTTP2_TYPE_DATA), - end ? " END," : "", - stream_id, - data.len()); + Http2::set_frame_header(buffer, SW_HTTP2_TYPE_DATA, data.len(), flag, stream_id); + 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; } @@ -1286,28 +1290,30 @@ 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); bool ret; - swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_GOAWAY, SW_HTTP2_GOAWAY_SIZE + debug_data_len, error_code, 0); + Http2::set_frame_header(frame, SW_HTTP2_TYPE_GOAWAY, SW_HTTP2_GOAWAY_SIZE + debug_data_len, error_code, 0); *(uint32_t *) (frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(last_stream_id); *(uint32_t *) (frame + SW_HTTP2_FRAME_HEADER_SIZE + 4) = htonl(error_code); 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", - swHttp2_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; @@ -1319,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; @@ -1327,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) @@ -1337,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; @@ -1344,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) { @@ -1356,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(swHttp2_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()); @@ -1405,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()); } @@ -1415,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; @@ -1433,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; @@ -1442,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; @@ -1472,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; @@ -1481,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 41c21ea3d7d..ddfe9103e71 100644 --- a/ext-src/swoole_http2_server.cc +++ b/ext-src/swoole_http2_server.cc @@ -10,66 +10,69 @@ | to obtain it through the world-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" #include "main/php_variables.h" -#include - using namespace swoole; using std::string; using swoole::coroutine::System; +using swoole::http2::get_default_setting; using swoole::http_server::StaticHandler; -using http_context = swoole::http::Context; -using Http2Stream = swoole::http2::Stream; -using Http2Session = swoole::http2::Session; +namespace Http2 = swoole::http2; + +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(http_context *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); - swoole_http_context_copy(client->default_ctx, ctx); + ctx->copy(client->default_ctx); ctx->http2 = true; 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() { ctx->stream = nullptr; - ctx->end = true; - swoole_http_context_free(ctx); + ctx->end_ = true; + ctx->free(); } 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); - swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_RST_STREAM, SW_HTTP2_RST_STREAM_SIZE, 0, id); + 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); } 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; @@ -87,26 +90,27 @@ Http2Session::~Session() { nghttp2_hd_deflate_del(deflater); } if (default_ctx) { - efree(default_ctx); + delete default_ctx; } http2_sessions.erase(fd); } -static void http2_server_send_window_update(http_context *ctx, uint32_t stream_id, uint32_t size) { +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); - swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_WINDOW_UPDATE, SW_HTTP2_WINDOW_UPDATE_SIZE, 0, stream_id); + 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(http_context *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); if (size > 0) { - http2::HeaderSet trailer(size); + Http2::HeaderSet trailer(size); zend_string *key; zval *zvalue; @@ -125,25 +129,26 @@ static ssize_t http2_build_trailer(http_context *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(http_context *ctx, uchar *buffer) { return 0; } -static bool swoole_http2_is_static_file(Server *serv, http_context *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, http_context *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; - swoole_http_response_set_header(ctx, 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, http_context *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) { - http_context *ctx = stream->ctx; +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,28 +274,53 @@ 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 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_build_header(http_context *ctx, uchar *buffer, size_t body_length) { +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(8 + php_swoole_array_length_safe(zheader) + php_swoole_array_length_safe(zcookie)); - char *date_str = nullptr; + Http2::HeaderSet headers(32 + php_swoole_array_length_safe(zheader) + php_swoole_array_length_safe(zcookie)); char intbuf[2][16]; int ret; - assert(ctx->send_header == 0); + assert(ctx->send_header_ == 0); // status code if (ctx->response.status == 0) { @@ -253,50 +329,81 @@ static ssize_t http2_build_header(http_context *ctx, uchar *buffer, size_t body_ ret = swoole_itoa(intbuf[0], ctx->response.status); headers.add(ZEND_STRL(":status"), intbuf[0], ret); + uint32_t header_flags = 0x0; + // headers if (ZVAL_IS_ARRAY(zheader)) { - uint32_t header_flag = 0x0; - zend_string *key; + const char *key; + uint32_t keylen; zval *zvalue; + int type; + + zend_string *content_type = nullptr; + auto add_header = + [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; + } + zend::String str_value(value); + str_value.rtrim(); + if (swoole_http_has_crlf(str_value.val(), str_value.len())) { + return; + } + if (SW_STRCASEEQ(key, l_key, "server")) { + header_flags |= HTTP_HEADER_SERVER; + } else if (SW_STRCASEEQ(key, l_key, "content-length")) { + return; // ignore + } else if (SW_STRCASEEQ(key, l_key, "date")) { + header_flags |= HTTP_HEADER_DATE; + } 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()); + }; - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zheader), key, zvalue) { + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zheader), key, keylen, type, zvalue) { if (UNEXPECTED(!key || ZVAL_IS_NULL(zvalue))) { continue; } - zend::String str_value(zvalue); - char *c_key = ZSTR_VAL(key); - size_t c_keylen = ZSTR_LEN(key); - if (SW_STREQ(c_key, c_keylen, "server")) { - header_flag |= HTTP_HEADER_SERVER; - } else if (SW_STREQ(c_key, c_keylen, "content-length")) { - continue; // ignore - } else if (SW_STREQ(c_key, c_keylen, "date")) { - header_flag |= HTTP_HEADER_DATE; - } else if (SW_STREQ(c_key, c_keylen, "content-type")) { - header_flag |= HTTP_HEADER_CONTENT_TYPE; + if (ZVAL_IS_ARRAY(zvalue)) { + zval *zvalue_2; + SW_HASHTABLE_FOREACH_START(Z_ARRVAL_P(zvalue), zvalue_2) { + add_header(headers, key, keylen, zvalue_2, header_flags); + } + SW_HASHTABLE_FOREACH_END(); + } else { + add_header(headers, key, keylen, zvalue, header_flags); } - headers.add(c_key, c_keylen, str_value.val(), str_value.len()); } - ZEND_HASH_FOREACH_END(); + SW_HASHTABLE_FOREACH_END(); + (void) type; - if (!(header_flag & HTTP_HEADER_SERVER)) { - headers.add(ZEND_STRL("server"), ZEND_STRL(SW_HTTP_SERVER_SOFTWARE)); - } - if (!(header_flag & 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)); - } - if (!(header_flag & HTTP_HEADER_CONTENT_TYPE)) { - headers.add(ZEND_STRL("content-type"), ZEND_STRL("text/html")); +#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); + } } - } else { +#endif + } + + if (!(header_flags & HTTP_HEADER_SERVER)) { headers.add(ZEND_STRL("server"), ZEND_STRL(SW_HTTP_SERVER_SOFTWARE)); - headers.add(ZEND_STRL("content-type"), ZEND_STRL("text/html")); - 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)); } - if (date_str) { - efree(date_str); + if (!(header_flags & HTTP_HEADER_DATE)) { + http2_server_set_date_header(&headers); + } + if (!(header_flags & HTTP_HEADER_CONTENT_TYPE)) { + headers.add(ZEND_STRL("content-type"), ZEND_STRL("text/html")); } // cookies @@ -311,29 +418,27 @@ static ssize_t http2_build_header(http_context *ctx, uchar *buffer, size_t body_ 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 = swoole_http_get_content_encoding(ctx); - 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; @@ -349,27 +454,27 @@ static ssize_t http2_build_header(http_context *ctx, uchar *buffer, size_t body_ */ 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; } - ctx->send_header = 1; + ctx->send_header_ = 1; return rv; } -int swoole_http2_server_ping(http_context *ctx) { +int swoole_http2_server_ping(HttpContext *ctx) { char frame[SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE]; - swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0); + Http2::set_frame_header(frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_NONE, 0); return ctx->send(ctx, frame, SW_HTTP2_FRAME_HEADER_SIZE + SW_HTTP2_FRAME_PING_PAYLOAD_SIZE) ? SW_OK : SW_ERR; } -int swoole_http2_server_goaway(http_context *ctx, zend_long error_code, const char *debug_data, size_t debug_data_len) { +int swoole_http2_server_goaway(HttpContext *ctx, 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); bool ret; Http2Session *client = http2_sessions[ctx->fd]; uint32_t last_stream_id = client->last_stream_id; - swHttp2_set_frame_header(frame, SW_HTTP2_TYPE_GOAWAY, SW_HTTP2_GOAWAY_SIZE + debug_data_len, error_code, 0); + Http2::set_frame_header(frame, SW_HTTP2_TYPE_GOAWAY, SW_HTTP2_GOAWAY_SIZE + debug_data_len, error_code, 0); *(uint32_t *) (frame + SW_HTTP2_FRAME_HEADER_SIZE) = htonl(last_stream_id); *(uint32_t *) (frame + SW_HTTP2_FRAME_HEADER_SIZE + 4) = htonl(error_code); if (debug_data_len > 0) { @@ -381,14 +486,15 @@ int swoole_http2_server_goaway(http_context *ctx, zend_long error_code, const ch 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(); /** +---------------+ @@ -405,52 +511,72 @@ 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) { - swHttp2_set_frame_header( + 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 { - swHttp2_set_frame_header(frame_header, SW_HTTP2_TYPE_HEADERS, bytes, SW_HTTP2_FLAG_END_HEADERS, id); + 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)) { - ctx->send_header = 0; + if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { + ctx->send_header_ = 0; return false; } 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; } - swHttp2_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; @@ -459,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) { - swHttp2_set_frame_header( + 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; } } @@ -475,81 +602,115 @@ bool Http2Stream::send_trailer() { return true; } -static bool swoole_http2_server_respond(http_context *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) { @@ -566,20 +727,141 @@ static bool swoole_http2_server_respond(http_context *ctx, String *body) { return !error; } -static bool http2_context_sendfile(http_context *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; } @@ -589,36 +871,37 @@ static bool http2_context_sendfile(http_context *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(); - swoole_http_response_set_header(ctx, 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? } } @@ -629,7 +912,7 @@ static bool http2_context_sendfile(http_context *ctx, const char *file, uint32_t } if (error) { - ctx->close(ctx); + close(this); } else { client->streams.erase(stream->id); delete stream; @@ -638,13 +921,26 @@ static bool http2_context_sendfile(http_context *ctx, const char *file, uint32_t return true; } -static int http2_parse_header(Http2Session *client, http_context *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; @@ -668,7 +964,7 @@ static int http2_parse_header(Http2Session *client, http_context *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; } @@ -678,8 +974,12 @@ static int http2_parse_header(Http2Session *client, http_context *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")) { @@ -717,15 +1017,17 @@ static int http2_parse_header(Http2Session *client, http_context *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; } - swoole_http_parse_form_data(ctx, (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")) { @@ -738,7 +1040,7 @@ static int http2_parse_header(Http2Session *client, http_context *ctx, int flags } #ifdef SW_HAVE_COMPRESSION else if (ctx->enable_compression && SW_STRCASEEQ((char *) nv.name, nv.namelen, "accept-encoding")) { - swoole_http_get_compression_method(ctx, (char *) nv.value, nv.valuelen); + ctx->set_compression_method((char *) nv.value, nv.valuelen); } #endif add_assoc_stringl_ex(zheader, (char *) nv.name, nv.namelen, (char *) nv.value, nv.valuelen); @@ -770,11 +1072,12 @@ int swoole_http2_server_parse(Http2Session *client, const char *buf) { } if (client->shutting_down) { - swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP2_STREAM_IGNORE, "ignore http2 stream#%d after sending goaway", stream_id); + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_HTTP2_STREAM_IGNORE, "ignore http2 stream#%d after sending goaway", stream_id); return retval; } - ssize_t length = swHttp2_get_length(buf); + ssize_t length = Http2::get_length(buf); buf += SW_HTTP2_FRAME_HEADER_SIZE; uint16_t id = 0; @@ -783,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); @@ -835,8 +1138,8 @@ 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")); - http_context *ctx; + swoole_http2_frame_trace_log("%s", (stream ? "exist stream" : "new stream")); + HttpContext *ctx; if (!stream) { stream = new Http2Stream(client, stream_id); if (sw_unlikely(!stream->ctx)) { @@ -851,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; } @@ -863,19 +1166,19 @@ 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); return SW_ERR; } stream = stream_iterator->second; - http_context *ctx = stream->ctx; + HttpContext *ctx = stream->ctx; 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; @@ -883,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; } } @@ -904,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; } } @@ -921,10 +1224,10 @@ 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]; - swHttp2_set_frame_header( + Http2::set_frame_header( ping_frame, SW_HTTP2_TYPE_PING, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE, SW_HTTP2_FLAG_ACK, stream_id); memcpy(ping_frame + SW_HTTP2_FRAME_HEADER_SIZE, buf, SW_HTTP2_FRAME_PING_PAYLOAD_SIZE); client->default_ctx->send( @@ -935,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 = (swServer *) 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 @@ -965,44 +1271,40 @@ 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 = (http_context *) ecalloc(1, sizeof(http_context)); - swoole_http_server_init_context(serv, client->default_ctx); + client->default_ctx = new HttpContext(); + client->default_ctx->init(serv); client->default_ctx->fd = session_id; 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; @@ -1022,16 +1324,23 @@ void swoole_http2_server_session_free(Connection *conn) { delete client; } -void swoole_http2_response_end(http_context *ctx, zval *zdata, zval *return_value) { - swString http_body = {}; +void HttpContext::http2_end(zval *zdata, zval *return_value) { + String 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; } - - RETURN_BOOL(swoole_http2_server_respond(ctx, &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 9afac29383d..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 | @@ -21,6 +21,7 @@ #include "php_swoole_http.h" #include "swoole_string.h" +#include "swoole_protocol.h" #include "swoole_file.h" #include "swoole_util.h" #include "swoole_websocket.h" @@ -31,34 +32,17 @@ SW_EXTERN_C_BEGIN #include "thirdparty/swoole_http_parser.h" +#include "stubs/php_swoole_http_client_coro_arginfo.h" -#include "ext/standard/basic_functions.h" -#include "ext/standard/php_http.h" #include "ext/standard/base64.h" -#ifdef SW_HAVE_ZLIB -#include -#endif - SW_EXTERN_C_END - -#ifdef SW_HAVE_BROTLI -#include -#endif - -using swoole::network::Address; -using swoole::coroutine::Socket; using swoole::File; using swoole::String; +using swoole::coroutine::Socket; +using swoole::network::Address; -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); +namespace WebSocket = swoole::websocket; 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); @@ -67,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, @@ -82,18 +65,29 @@ static const swoole_http_parser_settings http_parser_settings = }; // clang-format on -namespace swoole { namespace coroutine { -class HttpClient { +namespace swoole { +namespace coroutine { +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; @@ -104,31 +98,46 @@ 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 bool websocket_mask = true; // enable websocket mask + 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 + 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; - bool has_upload_files = false; /* 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 @@ -137,27 +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); @@ -173,16 +186,17 @@ class HttpClient { #endif 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 exec(std::string _path); + 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 = SW_WEBSOCKET_FLAG_FIN); + 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; @@ -203,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()); @@ -215,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()); @@ -227,26 +245,32 @@ 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 static zend_class_entry *swoole_http_client_coro_ce; static zend_object_handlers swoole_http_client_coro_handlers; @@ -254,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; }; @@ -295,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; @@ -455,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? */ @@ -484,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 @@ -495,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->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 @@ -555,25 +513,26 @@ 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)) != (ssize_t) http->body->length) { + if (http_client_co_write(http->download_file->get_fd(), SW_STRINGL(http->body)) != + (ssize_t) http->body->length) { return -1; } http->body->clear(); @@ -582,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) { @@ -608,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; @@ -620,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; } @@ -644,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; @@ -685,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; } @@ -695,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; } } @@ -719,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; } } @@ -732,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; } @@ -750,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); @@ -769,14 +776,26 @@ void HttpClient::apply_setting(zval *zset, const bool check_all) { if (php_swoole_array_get_value(vht, "websocket_mask", ztmp)) { websocket_mask = zval_is_true(ztmp); } + if (php_swoole_array_get_value(vht, "http_compression", ztmp)) { + http_compression = zval_is_true(ztmp); + } + if (php_swoole_array_get_value(vht, "body_decompression", ztmp)) { + body_decompression = zval_is_true(ztmp); + } #ifdef SW_HAVE_ZLIB if (php_swoole_array_get_value(vht, "websocket_compression", ztmp)) { 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 @@ -788,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); @@ -800,85 +819,135 @@ 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) { - zend_update_property_long( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), ENOMEM); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), swoole_strerror(ENOMEM)); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - 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"); - zend_update_property_long(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errCode"), errno); - zend_update_property_string( - swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), swoole_strerror(errno)); - zend_update_property_long(swoole_http_client_coro_ce, - SW_Z8_OBJ_P(zobject), - ZEND_STRL("statusCode"), - 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)) { - 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_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; } -bool HttpClient::keep_liveness() { +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); + zend_update_property_string(ce, obj, ZEND_STRL("errMsg"), msg); + zend_update_property_long(ce, obj, ZEND_STRL("statusCode"), status); +} + +bool Client::keep_liveness() { if (!socket || !socket->check_liveness()) { if (socket) { /* in progress */ socket->check_bound_co(SW_EVENT_RDWR); - 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); + 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; } @@ -888,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(); @@ -964,12 +1031,13 @@ 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(" ")); } // ============ path & proxy ============ + bool require_proxy_authentication = false; #ifdef SW_USE_OPENSSL if (socket->http_proxy && !socket->ssl_is_enable()) #else @@ -985,8 +1053,15 @@ bool HttpClient::send() { } size_t proxy_uri_len = path.length() + _host_len + strlen(pre) + 10; char *proxy_uri = (char *) emalloc(proxy_uri_len); - proxy_uri_len = sw_snprintf(proxy_uri, proxy_uri_len, "%s%s:%u%s", pre, _host, port, path.c_str()); + if (nullptr == memchr(_host, ':', _host_len)) { + proxy_uri_len = sw_snprintf(proxy_uri, proxy_uri_len, "%s%s:%u%s", pre, _host, port, path.c_str()); + } else { + proxy_uri_len = sw_snprintf(proxy_uri, proxy_uri_len, "%s%s%s", pre, _host, path.c_str()); + } buffer->append(proxy_uri, proxy_uri_len); + if (!socket->http_proxy->password.empty()) { + require_proxy_authentication = true; + } efree(proxy_uri); } else { buffer->append(path.c_str(), path.length()); @@ -1034,17 +1109,35 @@ bool HttpClient::send() { header_flag |= HTTP_HEADER_CONTENT_LENGTH; // ignore custom Content-Length value continue; - } else if (SW_STRCASEEQ(key, keylen, "Connection")) { - header_flag |= HTTP_HEADER_CONNECTION; - } else if (SW_STRCASEEQ(key, keylen, "Accept-Encoding")) { + } + + 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()); + + if (SW_STRCASEEQ(key, keylen, "Connection")) { + header_flag |= HTTP_HEADER_CONNECTION; + if (SW_STRCASEEQ(str_value.val(), str_value.len(), "close")) { + keep_alive = 0; + } + } } SW_HASHTABLE_FOREACH_END(); } - + // http proxy authentication + if (require_proxy_authentication) { + std::string value("Basic "); + value += socket->http_proxy->get_auth_str(); + add_headers(buffer, ZEND_STRL("Proxy-Authorization"), value.c_str(), value.length()); + } if (!basic_auth.empty()) { add_headers(buffer, ZEND_STRL("Authorization"), basic_auth.c_str(), basic_auth.size()); } @@ -1056,17 +1149,21 @@ bool HttpClient::send() { } } #ifdef SW_HAVE_COMPRESSION - if (!(header_flag & HTTP_HEADER_ACCEPT_ENCODING)) { + if (http_compression && !(header_flag & HTTP_HEADER_ACCEPT_ENCODING)) { add_headers(buffer, - ZEND_STRL("Accept-Encoding"), + ZEND_STRL("Accept-Encoding"), #if defined(SW_HAVE_ZLIB) && defined(SW_HAVE_BROTLI) - ZEND_STRL("gzip, deflate, br") + ZEND_STRL("gzip, deflate, br") #else #ifdef SW_HAVE_ZLIB - ZEND_STRL("gzip, deflate") + ZEND_STRL("gzip, deflate") #else #ifdef SW_HAVE_BROTLI - ZEND_STRL("br") + ZEND_STRL("br") +#else +#ifdef SW_HAVE_ZSTD + ZEND_STRL("zstd") +#endif #endif #endif #endif @@ -1093,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); @@ -1283,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 ============ @@ -1320,138 +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: - 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_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) { - this->path = 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_connect()) { - 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; + resolve_context_ = {}; + if (use_default_port) { + resolve_context_.with_port = true; } - 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 = swWebSocket_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_connect()) { - 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; - } - - 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 { - swString 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; @@ -1460,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)) { @@ -1473,19 +1494,21 @@ 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) { buffer->length += retval; - if (swoole_strnpos(buffer->str + header_crlf_offset, buffer->length - header_crlf_offset, ZEND_STRL("\r\n\r\n")) < 0) { + if (swoole_strnpos( + buffer->str + header_crlf_offset, buffer->length - header_crlf_offset, ZEND_STRL("\r\n\r\n")) < 0) { 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; @@ -1499,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) { @@ -1512,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 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 + zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("fd"), socket->get_fd()); + return true; } } -bool HttpClient::upgrade(std::string path) { +bool Client::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)); + 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 - exec(path); + if (websocket_compression) { + add_assoc_string(zheaders, "Sec-Websocket-Extensions", (char *) SW_WEBSOCKET_EXTENSION_DEFLATE); } - return websocket; +#endif + return exec(path); } -bool HttpClient::push(zval *zdata, zend_long opcode, uint8_t flags) { +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"); @@ -1556,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_connect()) { - 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 { @@ -1603,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; @@ -1620,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")); @@ -1634,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; @@ -1667,31 +1743,34 @@ 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) { - return (HttpClientObject *) ((char *) obj - swoole_http_client_coro_handlers.offset); +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) { - HttpClientObject *hcc = (HttpClientObject *) zend_object_alloc(sizeof(HttpClientObject ), 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); hcc->std.handlers = &swoole_http_client_coro_handlers; @@ -1701,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); @@ -1742,26 +1826,23 @@ 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 = 80; + zend_long port = 0; zend_bool ssl = 0; ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 3) @@ -1789,13 +1870,13 @@ static PHP_METHOD(swoole_http_client_coro, __construct) { RETURN_FALSE; } #endif - 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) @@ -1814,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) @@ -1861,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; @@ -2015,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; @@ -2027,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; @@ -2041,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; @@ -2058,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; @@ -2078,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; @@ -2090,11 +2171,15 @@ 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; + zend_long opcode = WebSocket::OPCODE_TEXT; zval *zflags = nullptr; - zend_long flags = SW_WEBSOCKET_FLAG_FIN; + zend_long flags = WebSocket::FLAG_FIN; ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_ZVAL(zdata) @@ -2106,12 +2191,16 @@ static PHP_METHOD(swoole_http_client_coro, push) { if (zflags != nullptr) { flags = zval_get_long(zflags); } - - RETURN_BOOL(phc->push(zdata, opcode, flags & SW_WEBSOCKET_FLAGS_ALL)); + 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) @@ -2119,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()); } @@ -2150,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 01c86e0afa1..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,28 +18,10 @@ SW_EXTERN_C_BEGIN #include "ext/standard/url.h" -#include "ext/standard/sha1.h" -#include "ext/standard/php_var.h" -#include "ext/standard/php_string.h" -#include "ext/standard/php_math.h" -#include "ext/standard/php_array.h" -#include "ext/date/php_date.h" -#include "ext/standard/md5.h" +#include "stubs/php_swoole_http_request_arginfo.h" +#include "thirdparty/php/main/SAPI.h" SW_EXTERN_C_END -#include "main/rfc1867.h" -#include "main/php_variables.h" - -#include "swoole_base64.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, @@ -48,11 +30,16 @@ 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 http_request = swoole::http::Request; -using http_context = swoole::http::Context; +using HttpRequest = swoole::http::Request; +using HttpContext = swoole::http::Context; +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); @@ -69,14 +56,14 @@ static int multipart_body_on_header_complete(multipart_parser *p); static int multipart_body_on_data_end(multipart_parser *p); static int http_request_on_path(swoole_http_parser *parser, const char *at, size_t length) { - http_context *ctx = (http_context *) parser->data; + HttpContext *ctx = (HttpContext *) parser->data; ctx->request.path = estrndup(at, length); ctx->request.path_len = length; 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('"') @@ -90,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 { @@ -102,69 +89,6 @@ static inline char *http_trim_double_quote(char *ptr, int *len) { return tmp; } -static sw_inline const char *http_get_method_name(int method) { - switch (method) { - case PHP_HTTP_GET: - return "GET"; - case PHP_HTTP_POST: - return "POST"; - case PHP_HTTP_HEAD: - return "HEAD"; - case PHP_HTTP_PUT: - return "PUT"; - case PHP_HTTP_DELETE: - return "DELETE"; - case PHP_HTTP_PATCH: - return "PATCH"; - case PHP_HTTP_CONNECT: - return "CONNECT"; - case PHP_HTTP_OPTIONS: - return "OPTIONS"; - case PHP_HTTP_TRACE: - return "TRACE"; - case PHP_HTTP_COPY: - return "COPY"; - case PHP_HTTP_LOCK: - return "LOCK"; - case PHP_HTTP_MKCOL: - return "MKCOL"; - case PHP_HTTP_MOVE: - return "MOVE"; - case PHP_HTTP_PROPFIND: - return "PROPFIND"; - case PHP_HTTP_PROPPATCH: - return "PROPPATCH"; - case PHP_HTTP_UNLOCK: - return "UNLOCK"; - /* subversion */ - case PHP_HTTP_REPORT: - return "REPORT"; - case PHP_HTTP_MKACTIVITY: - return "MKACTIVITY"; - case PHP_HTTP_CHECKOUT: - return "CHECKOUT"; - case PHP_HTTP_MERGE: - return "MERGE"; - /* upnp */ - case PHP_HTTP_MSEARCH: - return "MSEARCH"; - case PHP_HTTP_NOTIFY: - return "NOTIFY"; - case PHP_HTTP_SUBSCRIBE: - return "SUBSCRIBE"; - case PHP_HTTP_UNSUBSCRIBE: - return "UNSUBSCRIBE"; - /* proxy */ - case PHP_HTTP_PURGE: - return "PURGE"; - /* unknown */ - case PHP_HTTP_NOT_IMPLEMENTED: - return "UNKNOWN"; - default: - return nullptr; - } -} - // clang-format off static const swoole_http_parser_settings http_parser_settings = { @@ -192,60 +116,79 @@ static const multipart_parser_settings mt_parser_settings = }; // clang-format on -size_t swoole_http_requset_parse(http_context *ctx, const char *data, size_t length) { - return swoole_http_parser_execute(&ctx->parser, &http_parser_settings, data, length); +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 { - http_context *ctx; +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); } -http_context *php_swoole_http_request_get_context(zval *zobject) { +HttpContext *php_swoole_http_request_get_context(zval *zobject) { return php_swoole_http_request_fetch_object(Z_OBJ_P(zobject))->ctx; } -void php_swoole_http_request_set_context(zval *zobject, http_context *ctx) { +void php_swoole_http_request_set_context(zval *zobject, HttpContext *ctx) { php_swoole_http_request_fetch_object(Z_OBJ_P(zobject))->ctx = ctx; } static void php_swoole_http_request_free_object(zend_object *object) { - http_request_t *request = php_swoole_http_request_fetch_object(object); - http_context *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)); + HttpRequestObject *request = php_swoole_http_request_fetch_object(object); + HttpContext *ctx = request->ctx; + + 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; - swoole_http_context_free(ctx); + ctx->free(); } zend_object_std_dtor(&request->std); } 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; @@ -254,40 +197,40 @@ static zend_object *php_swoole_http_request_create_object(zend_class_entry *ce) SW_EXTERN_C_BEGIN static PHP_METHOD(swoole_http_request, getData); -static PHP_METHOD(swoole_http_request, rawContent); -static PHP_METHOD(swoole_http_request, __destruct); +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, getContent); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_http_void, 0, 0, 0) -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, __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); @@ -298,208 +241,187 @@ 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) { - http_context *ctx = (http_context *) parser->data; - add_assoc_stringl_ex(ctx->request.zserver, ZEND_STRL("query_string"), (char *) at, length); + HttpContext *ctx = (HttpContext *) parser->data; + + 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) { - http_context *ctx = (http_context *) parser->data; - ctx->current_header_name = (char *) at; + HttpContext *ctx = (HttpContext *) parser->data; + ctx->current_header_name = at; ctx->current_header_name_len = length; return 0; } -int swoole_http_parse_form_data(http_context *ctx, const char *boundary_str, int boundary_len) { - multipart_parser *mt_parser = multipart_parser_init(boundary_str, boundary_len, &mt_parser_settings); +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 SW_ERR; + return false; + } + form_data_buffer = new String(SW_BUFFER_SIZE_STD); + mt_parser->data = this; + return true; +} + +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; +} - ctx->mt_parser = mt_parser; - mt_parser->data = ctx; +void swoole_http_parse_cookie(zval *zcookies, const char *at, size_t length) { + if (length == 0) { + return; + } - return SW_OK; + swoole_php_treat_data(PARSE_COOKIE, estrndup(at, length), zcookies); } -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; +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); +} - char *_value; - int klen = 0; - int vlen = 0; - int state = -1; +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; + } - 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); + 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; - http_context *ctx = (http_context *) parser->data; + 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; } - swServer *serv = (swServer *) ctx->private_data; - swConnection *conn = serv->get_connection_by_session_id(ctx->fd); + Server *serv = (Server *) ctx->private_data; + if (!serv) { + goto _add_header; + } + 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; } - swListenPort *port = serv->get_port_by_server_fd(conn->server_fd); + ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); if (port->open_websocket_protocol) { - conn->websocket_status = WEBSOCKET_STATUS_CONNECTION; + conn->websocket_status = swoole::websocket::STATUS_CONNECTION; } } 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); - swoole_http_parse_form_data(ctx, 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")) { - swoole_http_get_compression_method(ctx, at, length); + 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; } static int http_request_on_headers_complete(swoole_http_parser *parser) { - http_context *ctx = (http_context *) parser->data; + 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; @@ -515,34 +437,40 @@ 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", (char *) 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; } static int multipart_body_on_header_field(multipart_parser *p, const char *at, size_t length) { - http_context *ctx = (http_context *) p->data; + HttpContext *ctx = (HttpContext *) p->data; return http_request_on_header_field(&ctx->parser, at, length); } 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; - http_context *ctx = (http_context *) p->data; + HttpContext *ctx = (HttpContext *) p->data; /** * Hash collision attack */ @@ -557,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)); @@ -593,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; @@ -603,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)); @@ -616,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) { - http_context *ctx = (http_context *) p->data; + 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; } @@ -673,7 +620,7 @@ static void get_random_file_name(char *des, const char *src) #endif static int multipart_body_on_header_complete(multipart_parser *p) { - http_context *ctx = (http_context *) p->data; + HttpContext *ctx = (HttpContext *) p->data; if (!ctx->current_input_name) { return 0; } @@ -688,7 +635,7 @@ static int multipart_body_on_header_complete(multipart_parser *p) { } char file_path[SW_HTTP_UPLOAD_TMPDIR_SIZE]; - sw_snprintf(file_path, SW_HTTP_UPLOAD_TMPDIR_SIZE, "%s/swoole.upfile.XXXXXX", ctx->upload_tmp_dir); + sw_snprintf(file_path, SW_HTTP_UPLOAD_TMPDIR_SIZE, "%s/swoole.upfile.XXXXXX", ctx->upload_tmp_dir.c_str()); int tmpfile = swoole_tmpfile(file_path); if (tmpfile < 0) { return 0; @@ -697,40 +644,33 @@ 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; } static int multipart_body_on_data_end(multipart_parser *p) { - http_context *ctx = (http_context *) p->data; + HttpContext *ctx = (HttpContext *) p->data; 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; } @@ -740,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) { @@ -797,7 +745,7 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size return 0; } - http_context *ctx = (http_context *) parser->data; + HttpContext *ctx = (HttpContext *) parser->data; bool is_beginning = (ctx->request.chunked_body ? ctx->request.chunked_body->length : ctx->request.body_length) == 0; if (ctx->recv_chunked) { @@ -806,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 { @@ -827,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; } } @@ -837,57 +778,78 @@ static int http_request_on_body(swoole_http_parser *parser, const char *at, size } static int http_request_message_complete(swoole_http_parser *parser) { - http_context *ctx = (http_context *) parser->data; + HttpContext *ctx = (HttpContext *) parser->data; size_t content_length = ctx->request.chunked_body ? ctx->request.chunked_body->length : ctx->request.body_length; 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 */ } #ifdef SW_HAVE_COMPRESSION -void swoole_http_get_compression_method(http_context *ctx, const char *accept_encoding, size_t length) { +void HttpContext::set_compression_method(const char *accept_encoding, size_t length) { #ifdef SW_HAVE_BROTLI if (swoole_strnpos(accept_encoding, length, ZEND_STRL("br")) >= 0) { - ctx->accept_compression = 1; - ctx->compression_method = HTTP_COMPRESS_BR; + accept_compression = 1; + compression_method = HTTP_COMPRESS_BR; } else #endif if (swoole_strnpos(accept_encoding, length, ZEND_STRL("gzip")) >= 0) { - ctx->accept_compression = 1; - ctx->compression_method = HTTP_COMPRESS_GZIP; + accept_compression = 1; + compression_method = HTTP_COMPRESS_GZIP; } else if (swoole_strnpos(accept_encoding, length, ZEND_STRL("deflate")) >= 0) { - ctx->accept_compression = 1; - ctx->compression_method = HTTP_COMPRESS_DEFLATE; + 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 { - ctx->accept_compression = 0; + accept_compression = 0; } } -const char *swoole_http_get_content_encoding(http_context *ctx) { - if (ctx->compression_method == HTTP_COMPRESS_GZIP) { +const char *HttpContext::get_content_encoding() { + if (compression_method == HTTP_COMPRESS_GZIP) { return "gzip"; - } else if (ctx->compression_method == HTTP_COMPRESS_DEFLATE) { + } else if (compression_method == HTTP_COMPRESS_DEFLATE) { return "deflate"; } #ifdef SW_HAVE_BROTLI - else if (ctx->compression_method == HTTP_COMPRESS_BR) { + 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; @@ -895,40 +857,35 @@ const char *swoole_http_get_content_encoding(http_context *ctx) { } #endif -static PHP_METHOD(swoole_http_request, rawContent) { - http_context *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); +static PHP_METHOD(swoole_http_request, getContent) { + HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } - http_request *req = &ctx->request; + HttpRequest *req = &ctx->request; if (req->body_length > 0) { zval *zdata = &req->zdata; 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(); } static PHP_METHOD(swoole_http_request, getData) { - http_context *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { 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); @@ -937,4 +894,121 @@ static PHP_METHOD(swoole_http_request, getData) { RETURN_EMPTY_STRING(); } -static PHP_METHOD(swoole_http_request, __destruct) {} +static PHP_METHOD(swoole_http_request, create) { + zval *zoptions = nullptr; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY(zoptions) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + HttpContext *ctx = new HttpContext(); + object_init_ex(return_value, swoole_http_request_ce); + zval *zrequest_object = &ctx->request._zobject; + ctx->request.zobject = zrequest_object; + *zrequest_object = *return_value; + php_swoole_http_request_set_context(zrequest_object, ctx); + + ctx->parse_cookie = 1; + ctx->parse_body = 1; + ctx->parse_files = 1; +#ifdef SW_HAVE_COMPRESSION + ctx->enable_compression = 1; + ctx->compression_level = SW_Z_BEST_SPEED; +#endif + ctx->upload_tmp_dir = "/tmp"; + + if (zoptions && ZVAL_IS_ARRAY(zoptions)) { + char *key; + uint32_t keylen; + int keytype; + zval *zvalue; + + SW_HASHTABLE_FOREACH_START2(Z_ARRVAL_P(zoptions), key, keylen, keytype, zvalue) { + if (SW_STRCASEEQ(key, keylen, "parse_cookie")) { + ctx->parse_cookie = zval_is_true(zvalue); + } else if (SW_STRCASEEQ(key, keylen, "parse_body")) { + ctx->parse_body = zval_is_true(zvalue); + } else if (SW_STRCASEEQ(key, keylen, "parse_files")) { + ctx->parse_files = zval_is_true(zvalue); + } +#ifdef SW_HAVE_COMPRESSION + else if (SW_STRCASEEQ(key, keylen, "enable_compression")) { + ctx->enable_compression = zval_is_true(zvalue); + } else if (SW_STRCASEEQ(key, keylen, "compression_level")) { + ctx->compression_level = zval_get_long(zvalue); + } +#endif +#ifdef SW_HAVE_ZLIB + else if (SW_STRCASEEQ(key, keylen, "websocket_compression")) { + ctx->websocket_compression = zval_is_true(zvalue); + } +#endif + else if (SW_STRCASEEQ(key, keylen, "upload_tmp_dir")) { + ctx->upload_tmp_dir = zend::String(zvalue).to_std_string(); + } + (void) keytype; + } + SW_HASHTABLE_FOREACH_END(); + } + + swoole_http_parser *parser = &ctx->parser; + parser->data = ctx; + swoole_http_parser_init(parser, PHP_HTTP_REQUEST); + + 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")); +} + +static PHP_METHOD(swoole_http_request, parse) { + HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx) || ctx->completed) { + RETURN_FALSE; + } + + char *str; + size_t l_str; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(str, l_str) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + + if (Z_TYPE(ctx->request.zdata) != IS_STRING) { + ZVAL_STRINGL(&ctx->request.zdata, str, l_str); + } else { + size_t len = Z_STRLEN(ctx->request.zdata) + l_str; + zend_string *new_str = zend_string_alloc(len + 1, 0); + memcpy(new_str->val, Z_STRVAL(ctx->request.zdata), Z_STRLEN(ctx->request.zdata)); + memcpy(new_str->val + Z_STRLEN(ctx->request.zdata), str, l_str); + new_str->val[len] = 0; + new_str->len = len; + zval_dtor(&ctx->request.zdata); + ZVAL_STR(&ctx->request.zdata, new_str); + } + + RETURN_LONG(ctx->parse(str, l_str)); +} + +static PHP_METHOD(swoole_http_request, getMethod) { + HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + 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) { + HttpContext *ctx = php_swoole_http_request_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { + RETURN_FALSE; + } + RETURN_BOOL(ctx->completed); +} diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index 5308119dd32..ec05b9d61a3 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -10,45 +10,33 @@ | to obtain it through the world-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" - -extern "C" { -#include "ext/standard/url.h" -#include "ext/standard/sha1.h" -#include "ext/standard/php_var.h" -#include "ext/standard/php_string.h" -#include "ext/standard/php_math.h" -#include "ext/standard/php_array.h" -#include "ext/date/php_date.h" -#include "ext/standard/md5.h" -} - -#include "swoole_base64.h" #include "swoole_util.h" -#ifdef SW_HAVE_ZLIB -#include -#endif +BEGIN_EXTERN_C() +#include "stubs/php_swoole_http_response_arginfo.h" +END_EXTERN_C() -#ifdef SW_HAVE_BROTLI -#include -#endif - -using swoole::coroutine::Socket; +using swoole::Connection; +using swoole::Server; +using swoole::String; using swoole::substr_len; -using http_response = swoole::http::Response; -using http_context = swoole::http::Context; +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(http_context *ctx, swString *response, size_t body_length); -static ssize_t http_build_trailer(http_context *ctx, swString *response); - static inline void http_header_key_format(char *key, int length) { int i, state = 0; for (i = 0; i < length; i++) { @@ -67,89 +55,66 @@ static inline void http_header_key_format(char *key, int length) { } } -static inline bool http_has_crlf(const char *value, size_t length) { - /* new line/NUL character safety check */ - for (size_t i = 0; i < length; i++) { - /* RFC 7230 ch. 3.2.4 deprecates folding support */ - if (value[i] == '\n' || value[i] == '\r') { - php_swoole_error(E_WARNING, "Header may not contain more than a single header, new line detected"); - return true; - } - if (value[i] == '\0') { - php_swoole_error(E_WARNING, "Header may not contain NUL bytes"); - return true; - } - } - - return false; -} - -static inline swString *http_get_write_buffer(http_context *ctx) { - if (ctx->co_socket) { - swString *buffer = ((Socket *) ctx->private_data)->get_write_buffer(); - if (buffer != nullptr) { - return buffer; +String *HttpContext::get_write_buffer() { + if (co_socket) { + 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 { - http_context *ctx; +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); } -http_context *php_swoole_http_response_get_context(zval *zobject) { +HttpContext *php_swoole_http_response_get_context(zval *zobject) { return php_swoole_http_response_fetch_object(Z_OBJ_P(zobject))->ctx; } -void php_swoole_http_response_set_context(zval *zobject, http_context *ctx) { +void php_swoole_http_response_set_context(zval *zobject, HttpContext *ctx) { php_swoole_http_response_fetch_object(Z_OBJ_P(zobject))->ctx = ctx; } static void php_swoole_http_response_free_object(zend_object *object) { - http_response_t *response = php_swoole_http_response_fetch_object(object); - http_context *ctx = response->ctx; + 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) { - swoole_http2_response_end(ctx, nullptr, &ztmp); + ctx->http2_end(nullptr, &ztmp); } - } else -#endif - { - if (ctx->co_socket) { - swoole_http_response_end(ctx, nullptr, &ztmp); - } else { - swServer *serv = (swServer *) ctx->private_data; - swConnection *conn = serv->get_connection_by_session_id(ctx->fd); - if (conn && !conn->closed && !conn->peer_closed) { - swoole_http_response_end(ctx, nullptr, &ztmp); - } + } else { + if (ctx->is_available()) { + ctx->end(nullptr, &ztmp); } } } ctx->response.zobject = nullptr; - swoole_http_context_free(ctx); + ctx->free(); } zend_object_std_dtor(&response->std); } 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; @@ -165,6 +130,7 @@ static PHP_METHOD(swoole_http_response, cookie); static PHP_METHOD(swoole_http_response, rawcookie); static PHP_METHOD(swoole_http_response, header); static PHP_METHOD(swoole_http_response, initHeader); +static PHP_METHOD(swoole_http_response, isWritable); static PHP_METHOD(swoole_http_response, detach); static PHP_METHOD(swoole_http_response, create); /** @@ -174,123 +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, 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); @@ -302,237 +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; - } - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); - if (UNEXPECTED(!ctx)) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_ZVAL(zdata) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); -#ifdef SW_USE_HTTP2 - if (ctx->http2) { - php_swoole_error(E_WARNING, "HTTP2 client does not support HTTP-CHUNK"); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + if (UNEXPECTED(!ctx)) { RETURN_FALSE; } -#endif #ifdef SW_HAVE_COMPRESSION - ctx->accept_compression = 0; -#endif - - swString *http_buffer = http_get_write_buffer(ctx); - - 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(http_context *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; - } else if (SW_STRCASEEQ(key, keylen, "Content-Length") && ctx->parser.method != PHP_HTTP_HEAD) { - return false; + return HTTP_HEADER_DATE; + } else if (SW_STRCASEEQ(key, keylen, "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(http_context *ctx, swString *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 (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 - n = sw_snprintf(buf, l_buf, "Content-Length: %zu\r\n", body_length); - response->append(buf, n); - } -#ifdef SW_HAVE_COMPRESSION - // http compress - if (ctx->accept_compression) { - const char *content_encoding = swoole_http_get_content_encoding(ctx); - response->append(ZEND_STRL("Content-Encoding: ")); - response->append((char *) content_encoding, strlen(content_encoding)); - response->append(ZEND_STRL("\r\n")); + if (!(header_flags & HTTP_HEADER_CONTENT_LENGTH)) { + 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")); + } } -#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(http_context *ctx, swString *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) { @@ -550,13 +470,13 @@ static ssize_t http_build_trailer(http_context *ctx, swString *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; @@ -583,111 +503,134 @@ 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 static PHP_METHOD(swoole_http_response, initHeader) { - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } @@ -701,8 +644,16 @@ static PHP_METHOD(swoole_http_response, initHeader) { RETURN_TRUE; } +static PHP_METHOD(swoole_http_response, isWritable) { + HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS); + if (!ctx || (ctx->end_ || ctx->detached)) { + RETURN_FALSE; + } + RETURN_TRUE; +} + static PHP_METHOD(swoole_http_response, end) { - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } @@ -714,183 +665,250 @@ 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) { - swoole_http2_response_end(ctx, zdata, return_value); - } else -#endif - { - swoole_http_response_end(ctx, zdata, return_value); + ctx->http2_end(zdata, return_value); + } else { + ctx->end(zdata, return_value); } } -void swoole_http_response_send_trailer(http_context *ctx, zval *return_value) { - swString *http_buffer = http_get_write_buffer(ctx); +void HttpContext::send_trailer(zval *return_value) { + String *http_buffer = get_write_buffer(); http_buffer->clear(); - if (http_build_trailer(ctx, http_buffer) == 0) { + if (build_trailer(http_buffer) == 0) { return; } - if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { - ctx->end = 1; - ctx->close(ctx); + if (!send(this, http_buffer->str, http_buffer->length)) { + end_ = 1; + close(this); RETURN_FALSE; } } -void swoole_http_response_end(http_context *ctx, 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 (ctx->send_chunked) { - if (ctx->send_trailer) { - if (!ctx->send(ctx, ZEND_STRL("0\r\n"))) { + 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; } - swoole_http_response_send_trailer(ctx, return_value); - ctx->send_trailer = 0; + } + if (send_trailer_) { + if (!send(this, ZEND_STRL("0\r\n"))) { + RETURN_FALSE; + } + send_trailer(return_value); + send_trailer_ = 0; } else { - if (!ctx->send(ctx, ZEND_STRL("0\r\n\r\n"))) { + if (!send(this, ZEND_STRL("0\r\n\r\n"))) { RETURN_FALSE; } } - ctx->send_chunked = 0; - } - // no http chunk - else { - swString *http_buffer = http_get_write_buffer(ctx); + send_chunked = 0; + } 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 (ctx->accept_compression) { - if (http_body.length == 0 || - swoole_http_response_compress( - http_body.str, http_body.length, ctx->compression_method, ctx->compression_level) != SW_OK) { - ctx->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(ctx, 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 (ctx->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) { - ctx->send_header = 0; + // 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; } - } -#ifdef SW_HTTP_SEND_TWICE - else { - if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { - ctx->send_header = 0; + if (!send(this, data, length)) { + end_ = 1; + close(this); RETURN_FALSE; } - if (!ctx->send(ctx, send_body_str, send_body_len)) { - ctx->end = 1; - ctx->close(ctx); + goto _skip_copy; + } else { + if (http_buffer->append(data, length) < 0) { + send_header_ = 0; RETURN_FALSE; } - goto _skip_copy; } -#endif } - if (!ctx->send(ctx, http_buffer->str, http_buffer->length)) { - ctx->end = 1; - ctx->close(ctx); + if (!send(this, http_buffer->str, http_buffer->length)) { + end_ = 1; + close(this); RETURN_FALSE; } } -#ifdef SW_HTTP_SEND_TWICE _skip_copy: -#endif - if (ctx->upgrade && !ctx->co_socket) { - swServer *serv = (swServer *) ctx->private_data; - swConnection *conn = serv->get_connection_by_session_id(ctx->fd); - if (conn && conn->websocket_status == WEBSOCKET_STATUS_HANDSHAKE) { - if (ctx->response.status == 101) { - conn->websocket_status = WEBSOCKET_STATUS_ACTIVE; + 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; } else { /* connection should be closed when handshake failed */ - conn->websocket_status = WEBSOCKET_STATUS_NONE; - ctx->keepalive = 0; + conn->websocket_status = websocket::STATUS_NONE; + keepalive = 0; } } } - if (!ctx->keepalive) { - ctx->close(ctx); + if (!keepalive) { + close(this); } - ctx->end = 1; + end_ = 1; RETURN_TRUE; } -bool swoole_http_response_set_header(http_context *ctx, 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 swoole_http_response_set_header(ctx, k, klen, &ztmp, format); +bool HttpContext::set_header(const char *k, size_t klen, const char *v, size_t vlen, bool format) { + zend::Variable ztmp(v, vlen); + return set_header(k, klen, ztmp.ptr(), format); } -bool swoole_http_response_set_header(http_context *ctx, const char *k, size_t klen, zval *zvalue, bool 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 (http_has_crlf(k, klen)) { - Z_TRY_DELREF_P(zvalue); + if (swoole_http_has_crlf(k, klen)) { return false; } zval *zheader = swoole_http_init_and_read_property( - swoole_http_response_ce, ctx->response.zobject, &ctx->response.zheader, ZEND_STRL("header")); + 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 (ctx->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; } static PHP_METHOD(swoole_http_response, sendfile) { - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } @@ -905,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"); @@ -919,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; @@ -931,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 - swString *http_buffer = http_get_write_buffer(ctx); - - 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); - http_context *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 && 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); - } - } - 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); + php_swoole_error(E_WARNING, "The first argument must be a string or an cookie object"); + result = false; } - 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) { @@ -1092,7 +1052,7 @@ static PHP_METHOD(swoole_http_response, status) { Z_PARAM_STRING(reason, reason_len) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } @@ -1115,15 +1075,13 @@ static PHP_METHOD(swoole_http_response, header) { Z_PARAM_BOOL(format) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } - Z_TRY_ADDREF_P(zvalue); - RETURN_BOOL(swoole_http_response_set_header(ctx, k, klen, zvalue, format)); + 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; @@ -1134,7 +1092,7 @@ static PHP_METHOD(swoole_http_response, trailer) { Z_PARAM_STRING_EX(v, vlen, 1, 0) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (!ctx) { RETURN_FALSE; } @@ -1151,12 +1109,12 @@ static PHP_METHOD(swoole_http_response, trailer) { } else { add_assoc_stringl_ex(ztrailer, key_buf, klen, v, vlen); } - ctx->send_trailer = 1; + ctx->send_trailer_ = 1; RETURN_TRUE; } static PHP_METHOD(swoole_http_response, ping) { - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } @@ -1168,7 +1126,7 @@ static PHP_METHOD(swoole_http_response, ping) { } static PHP_METHOD(swoole_http_response, goaway) { - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } @@ -1176,20 +1134,22 @@ 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) { - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } @@ -1201,7 +1161,7 @@ static PHP_METHOD(swoole_http_response, upgrade) { } static PHP_METHOD(swoole_http_response, push) { - http_context *ctx = php_swoole_http_response_get_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { swoole_set_last_error(SW_ERROR_SESSION_CLOSED); RETURN_FALSE; @@ -1212,9 +1172,9 @@ static PHP_METHOD(swoole_http_response, push) { } zval *zdata; - zend_long opcode = WEBSOCKET_OPCODE_TEXT; + zend_long opcode = WebSocket::OPCODE_TEXT; zval *zflags = nullptr; - zend_long flags = SW_WEBSOCKET_FLAG_FIN; + zend_long flags = WebSocket::FLAG_FIN; ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_ZVAL(zdata) @@ -1227,7 +1187,7 @@ static PHP_METHOD(swoole_http_response, push) { flags = zval_get_long(zflags); } - swString *http_buffer = http_get_write_buffer(ctx); + String *http_buffer = ctx->get_write_buffer(); http_buffer->clear(); if (php_swoole_websocket_frame_is_object(zdata)) { if (php_swoole_websocket_frame_object_pack(http_buffer, zdata, 0, ctx->websocket_compression) < 0) { @@ -1235,7 +1195,7 @@ static PHP_METHOD(swoole_http_response, push) { } } else { if (php_swoole_websocket_frame_pack( - http_buffer, zdata, opcode, flags & SW_WEBSOCKET_FLAGS_ALL, 0, ctx->websocket_compression) < 0) { + http_buffer, zdata, opcode, flags & WebSocket::FLAGS_ALL, 0, ctx->websocket_compression) < 0) { RETURN_FALSE; } } @@ -1243,7 +1203,7 @@ static PHP_METHOD(swoole_http_response, push) { } static PHP_METHOD(swoole_http_response, close) { - http_context *ctx = php_swoole_http_response_get_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { swoole_set_last_error(SW_ERROR_SESSION_CLOSED); RETURN_FALSE; @@ -1252,7 +1212,7 @@ static PHP_METHOD(swoole_http_response, close) { } static PHP_METHOD(swoole_http_response, recv) { - http_context *ctx = php_swoole_http_response_get_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { swoole_set_last_error(SW_ERROR_SESSION_CLOSED); RETURN_FALSE; @@ -1271,7 +1231,7 @@ static PHP_METHOD(swoole_http_response, recv) { Socket *sock = (Socket *) ctx->private_data; ssize_t retval = sock->recv_packet(timeout); - swString _tmp; + String _tmp; if (retval < 0) { swoole_set_last_error(sock->errCode); @@ -1293,7 +1253,7 @@ static PHP_METHOD(swoole_http_response, recv) { } static PHP_METHOD(swoole_http_response, detach) { - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (!ctx) { RETURN_FALSE; } @@ -1302,50 +1262,97 @@ static PHP_METHOD(swoole_http_response, detach) { } static PHP_METHOD(swoole_http_response, create) { - zval *zserver = nullptr; - zend_long fd; - swServer *serv; + zval *zobject = nullptr; + zval *zrequest = nullptr; + zend_long fd = -1; + Server *serv = nullptr; + Socket *sock = nullptr; + HttpContext *ctx = nullptr; - if (ZEND_NUM_ARGS() == 1) { - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(fd) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_ZVAL(zobject) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(fd) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - serv = sw_server(); + if (ZVAL_IS_OBJECT(zobject)) { + _type_detect: + 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 (%ld) must be valid connection session_id", (long) fd); + RETURN_FALSE; + } + } else if (sw_zval_is_co_socket(zobject)) { + sock = php_swoole_get_socket(zobject); + fd = sock->get_fd(); + } else { + _bad_type: + php_swoole_fatal_error(E_WARNING, "parameter $1 must be instanceof Server or Coroutine\\Socket"); + RETURN_FALSE; + } + } else if (ZVAL_IS_ARRAY(zobject)) { + zrequest = zend_hash_index_find(Z_ARR_P(zobject), 1); + if (!ZVAL_IS_OBJECT(zrequest) || !instanceof_function(Z_OBJCE_P(zrequest), swoole_http_request_ce)) { + php_swoole_fatal_error(E_WARNING, "parameter $1.second must be instanceof Http\\Request"); + RETURN_FALSE; + } + zobject = zend_hash_index_find(Z_ARR_P(zobject), 0); + if (!ZVAL_IS_OBJECT(zobject)) { + goto _bad_type; + } else { + ctx = php_swoole_http_request_get_context(zrequest); + goto _type_detect; + } } else { - ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_OBJECT_OF_CLASS(zserver, swoole_server_ce) - Z_PARAM_LONG(fd) - ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - - serv = php_swoole_server_get_and_check_server(zserver); + fd = zval_get_long(zobject); + serv = sw_server(); } - if (serv == nullptr || !serv->is_started()) { + if (serv && !serv->is_started()) { php_swoole_fatal_error(E_WARNING, "server is not running"); RETURN_FALSE; } - http_context *ctx = (http_context *) ecalloc(1, sizeof(http_context)); if (!ctx) { - RETURN_FALSE; - } - - ctx->fd = fd; - ctx->keepalive = 1; - - swoole_http_server_init_context(sw_server(), ctx); - - if (sw_unlikely(swoole_http_buffer == nullptr)) { - php_swoole_http_server_init_global_variant(); + ctx = new HttpContext(); + ctx->keepalive = 1; + + if (serv) { + ctx->init(serv); + } else if (sock) { + ctx->init(sock); + ctx->parser.data = ctx; + swoole_http_parser_init(&ctx->parser, PHP_HTTP_REQUEST); + } else { + delete ctx; + assert(0); + RETURN_FALSE; + } + } else { + if (serv) { + ctx->bind(serv); + } else if (sock) { + ctx->bind(sock); + } else { + assert(0); + RETURN_FALSE; + } } object_init_ex(return_value, swoole_http_response_ce); php_swoole_http_response_set_context(return_value, ctx); + ctx->fd = fd; ctx->response.zobject = return_value; 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_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); + } } static PHP_METHOD(swoole_http_response, redirect) { @@ -1358,7 +1365,7 @@ static PHP_METHOD(swoole_http_response, redirect) { Z_PARAM_ZVAL_EX(zhttp_code, 1, 0) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - http_context *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); + HttpContext *ctx = php_swoole_http_response_get_and_check_context(ZEND_THIS); if (UNEXPECTED(!ctx)) { RETURN_FALSE; } @@ -1377,7 +1384,5 @@ static PHP_METHOD(swoole_http_response, redirect) { if (!Z_BVAL_P(return_value)) { return; } - swoole_http_response_end(ctx, nullptr, return_value); + 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 a44269dbf05..7e1eee796e3 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -10,129 +10,166 @@ | to obtain it through the world-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 "main/rfc1867.h" +#include "swoole_process_pool.h" +BEGIN_EXTERN_C() +#include "rfc1867.h" +END_EXTERN_C() using namespace swoole; using swoole::coroutine::Socket; -using http_request = swoole::http::Request; -using http_response = swoole::http::Response; -using http_context = swoole::http::Context; -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; +using HttpRequest = swoole::http::Request; +using HttpResponse = swoole::http::Response; +using HttpContext = swoole::http::Context; + +namespace WebSocket = swoole::websocket; zend_class_entry *swoole_http_server_ce; zend_object_handlers swoole_http_server_handlers; -static bool http_context_send_data(http_context *ctx, const char *data, size_t length); -static bool http_context_sendfile(http_context *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); -static bool http_context_disconnect(http_context *ctx); +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_onReceive(Server *serv, RecvData *req) { +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; } ListenPort *port = serv->get_port_by_server_fd(server_fd); // other server port - if (!port->open_http_protocol) { - return php_swoole_onReceive(serv, req); + if (!(port->open_http_protocol && php_swoole_server_isset_callback(serv, port, SW_SERVER_CB_onRequest)) && + !(port->open_websocket_protocol && php_swoole_server_isset_callback(serv, port, SW_SERVER_CB_onMessage))) { + return php_swoole_server_onReceive(serv, req); } // websocket client - if (conn->websocket_status == WEBSOCKET_STATUS_ACTIVE) { + 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 - http_context *ctx = swoole_http_context_new(session_id); - swoole_http_server_init_context(serv, ctx); + 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; swoole_http_parser_init(parser, PHP_HTTP_REQUEST); - size_t parsed_n = swoole_http_requset_parse(ctx, Z_STRVAL_P(zdata), Z_STRLEN_P(zdata)); + 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) { + if (conn->websocket_status == WebSocket::STATUS_CONNECTION) { + 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 { - conn->websocket_status = WEBSOCKET_STATUS_HANDSHAKE; + conn->websocket_status = WebSocket::STATUS_HANDSHAKE; 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: @@ -142,99 +179,148 @@ int php_swoole_http_onReceive(Server *serv, RecvData *req) { return SW_OK; } -void php_swoole_http_onClose(Server *serv, DataHead *ev) { - Connection *conn = serv->get_connection_by_session_id(ev->fd); - if (!conn) { - return; - } - php_swoole_onClose(serv, ev); -#ifdef SW_USE_HTTP2 - if (conn->http2_stream) { - swoole_http2_server_session_free(conn); - } -#endif +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); } -http_context *swoole_http_context_new(SessionId fd) { - http_context *ctx = (http_context *) ecalloc(1, sizeof(http_context)); +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); - -#if PHP_MEMORY_DEBUG - php_vmstat.new_http_request++; -#endif + 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; } -void swoole_http_server_init_context(Server *serv, http_context *ctx) { - ctx->parse_cookie = serv->http_parse_cookie; - ctx->parse_body = serv->http_parse_post; - ctx->parse_files = serv->http_parse_files; +void HttpContext::init(Server *serv) { + parse_cookie = serv->http_parse_cookie; + parse_body = serv->http_parse_post; + parse_files = serv->http_parse_files; #ifdef SW_HAVE_COMPRESSION - ctx->enable_compression = serv->http_compression; - ctx->compression_level = serv->http_compression_level; + 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 - ctx->private_data = serv; - ctx->upload_tmp_dir = serv->upload_tmp_dir.c_str(); - ctx->send = http_context_send_data; - ctx->sendfile = http_context_sendfile; - ctx->close = http_context_disconnect; + upload_tmp_dir = serv->upload_tmp_dir; + bind(serv); } -void swoole_http_context_copy(http_context *src, http_context *dst) { - dst->parse_cookie = src->parse_cookie; - dst->parse_body = src->parse_body; - dst->parse_files = src->parse_files; +void HttpContext::bind(Server *serv) { + private_data = serv; + send = http_context_send_data; + sendfile = http_context_sendfile; + close = http_context_disconnect; +} + +void HttpContext::copy(HttpContext *ctx) { + parse_cookie = ctx->parse_cookie; + parse_body = ctx->parse_body; + parse_files = ctx->parse_files; #ifdef SW_HAVE_COMPRESSION - dst->enable_compression = src->enable_compression; - dst->compression_level = src->compression_level; + enable_compression = ctx->enable_compression; + compression_level = ctx->compression_level; + compression_min_length = ctx->compression_min_length; + compression_types = ctx->compression_types; #endif - dst->private_data = src->private_data; - dst->upload_tmp_dir = src->upload_tmp_dir; - dst->send = src->send; - dst->sendfile = src->sendfile; - dst->close = src->close; + co_socket = ctx->co_socket; + private_data = ctx->private_data; + upload_tmp_dir = ctx->upload_tmp_dir; + 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 swoole_http_context_free(http_context *ctx) { +void HttpContext::free() { /* http context can only be free'd after request and response were free'd */ - if (ctx->request.zobject || ctx->response.zobject) { + if (request.zobject || response.zobject) { return; } -#ifdef SW_USE_HTTP2 - if (ctx->stream) { + if (stream) { return; } -#endif - http_request *req = &ctx->request; - http_response *res = &ctx->response; + HttpRequest *req = &request; + HttpResponse *res = &response; if (req->path) { efree(req->path); } @@ -244,63 +330,126 @@ void swoole_http_context_free(http_context *ctx) { 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); } - efree(ctx); -} - -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; } -http_context *php_swoole_http_request_get_and_check_context(zval *zobject) { - http_context *ctx = php_swoole_http_request_get_context(zobject); +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; } -http_context *php_swoole_http_response_get_and_check_context(zval *zobject) { - http_context *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)"); +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)) { + swoole_set_last_error(SW_ERROR_HTTP_CONTEXT_UNAVAILABLE); return nullptr; } return ctx; } -bool http_context_send_data(http_context *ctx, const char *data, size_t length) { +bool http_context_send_data(HttpContext *ctx, const char *data, size_t length) { Server *serv = (Server *) ctx->private_data; bool retval = serv->send(ctx->fd, (void *) data, length); if (!retval && swoole_get_last_error() == SW_ERROR_OUTPUT_SEND_YIELD) { 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); - } else { - return true; } + return retval; +} + +static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length) { + Server *serv = (Server *) ctx->private_data; + return serv->sendfile(ctx->fd, file, l_file, offset, length); } -static bool http_context_sendfile(http_context *ctx, const char *file, uint32_t l_file, off_t offset, size_t length) { +static bool http_context_disconnect(HttpContext *ctx) { Server *serv = (Server *) ctx->private_data; - return serv->sendfile(ctx->fd, file, l_file, offset, length) == SW_OK; + return serv->close(ctx->fd, 0); } -static bool http_context_disconnect(http_context *ctx) { +bool swoole_http_server_onBeforeRequest(HttpContext *ctx) { + ctx->onBeforeRequest = nullptr; + ctx->onAfterResponse = swoole_http_server_onAfterResponse; Server *serv = (Server *) ctx->private_data; - return serv->close(ctx->fd, 0) == SW_OK; + 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 b77ac72d340..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 | +----------------------------------------------------------------------+ */ @@ -18,69 +18,77 @@ #include #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; -using swoole::microtime; -using http_request = swoole::http::Request; -using http_response = swoole::http::Response; -using http_context = swoole::http::Context; +using HttpRequest = swoole::http::Request; +using HttpResponse = swoole::http::Response; +using HttpContext = swoole::http::Context; -#ifdef SW_USE_HTTP2 -using Http2Stream = swoole::http2::Stream; -using Http2Session = swoole::http2::Session; -#endif +namespace http2 = swoole::http2; +using Http2Stream = http2::Stream; +using Http2Session = http2::Session; static zend_class_entry *swoole_http_server_coro_ce; static zend_object_handlers swoole_http_server_coro_handlers; -static bool http_context_send_data(http_context *ctx, const char *data, size_t length); -static bool http_context_sendfile(http_context *ctx, const char *file, uint32_t l_file, off_t offset, size_t length); -static bool http_context_disconnect(http_context *ctx); +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); -#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 websocket_compression = false; @@ -88,69 +96,80 @@ 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(http_context *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; } - http_context *create_context(Socket *conn, zval *zconn) { - http_context *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; + HttpContext *create_context(Socket *conn, zval *zconn) { + HttpContext *ctx = swoole_http_context_new(conn->get_fd()); + 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; #endif - ctx->private_data = conn; - ctx->co_socket = 1; - ctx->send = http_context_send_data; - ctx->sendfile = http_context_sendfile; - ctx->close = http_context_disconnect; ctx->upload_tmp_dir = upload_tmp_dir; + ctx->bind(conn); + swoole_http_parser *parser = &ctx->parser; 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(http_context *ctx) { + void recv_http2_frame(HttpContext *ctx) { Socket *sock = (Socket *) ctx->private_data; - swHttp2_send_setting_frame(&sock->protocol, sock->get_socket()); + http2::send_setting_frame(&sock->protocol, sock->get_socket()); sock->open_length_check = true; sock->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE; sock->protocol.package_length_offset = 0; sock->protocol.package_body_offset = 0; - sock->protocol.get_package_length = swHttp2_get_frame_length; + sock->protocol.get_package_length = http2::get_frame_length; Http2Session session(ctx->fd); session.default_ctx = ctx; @@ -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; } @@ -248,53 +247,68 @@ static inline void http_server_set_error(zval *zobject, Socket *sock) { zend_update_property_string(swoole_http_server_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("errMsg"), sock->errMsg); } -static bool http_context_send_data(http_context *ctx, const char *data, size_t length) { +static bool http_context_send_data(HttpContext *ctx, const char *data, size_t length) { Socket *sock = (Socket *) ctx->private_data; return sock->send_all(data, length) == (ssize_t) length; } -static bool http_context_sendfile(http_context *ctx, const char *file, uint32_t l_file, off_t offset, size_t length) { +static bool http_context_sendfile(HttpContext *ctx, const char *file, uint32_t l_file, off_t offset, size_t length) { Socket *sock = (Socket *) ctx->private_data; return sock->sendfile(file, offset, length); } -static bool http_context_disconnect(http_context *ctx) { +static bool http_context_disconnect(HttpContext *ctx) { Socket *sock = (Socket *) ctx->private_data; return sock->close(); } 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); } +void HttpContext::init(Socket *sock) { + parse_cookie = 1; + parse_body = 1; + parse_files = 1; +#ifdef SW_HAVE_COMPRESSION + enable_compression = 1; + compression_level = SW_Z_BEST_SPEED; +#endif +#ifdef SW_HAVE_ZLIB + websocket_compression = 0; +#endif + upload_tmp_dir = "/tmp"; + bind(sock); +} + +void HttpContext::bind(Socket *sock) { + private_data = sock; + co_socket = 1; + send = http_context_send_data; + sendfile = http_context_sendfile; + close = http_context_disconnect; +} + 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); @@ -329,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) { @@ -373,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) { @@ -405,15 +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"); - if (!sw_zend_is_callable_at_frame(&zcallback, ZEND_THIS, execute_data, 0, &func_name, nullptr, &fci_cache, nullptr)) { + zend::Variable zcallback("onAccept"); + if (!sw_zend_is_callable_at_frame( + 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; } @@ -427,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) { @@ -450,7 +464,27 @@ 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, "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 @@ -471,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; @@ -503,94 +537,112 @@ 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(); - http_context *ctx = nullptr; + 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: { - ssize_t retval = sock->recv(buffer->str + buffer->length, buffer->size - buffer->length); - if (sw_unlikely(retval <= 0)) { - break; - } - buffer->length += retval; + _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; } + buffer->length += retval; + } - _parse_request: + _parse_request: if (!ctx) { ctx = hs->create_context(sock, zconn); } 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 = swoole_http_requset_parse(ctx, 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); @@ -599,22 +651,24 @@ static PHP_METHOD(swoole_http_server_coro, onAccept) { ctx = nullptr; break; } -#endif - size_t total_length = buffer->offset; - sw_set_zend_string(&ctx->request.zdata, buffer->pop(SW_BUFFER_SIZE_BIG), total_length); + 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 { @@ -625,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; @@ -641,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) { - http_context *ctx = stream->ctx; - http_server *hs = (http_server *) session->private_data; + HttpContext *ctx = stream->ctx; + HttpServer *hs = (HttpServer *) session->private_data; Socket *sock = (Socket *) ctx->private_data; zval *zserver = ctx->request.zserver; @@ -676,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)); } @@ -691,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 0445250a935..fdd6806d379 100644 --- a/ext-src/swoole_lock.cc +++ b/ext-src/swoole_lock.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 "php_swoole.h" +#include "php_swoole_private.h" #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 2ef85bb3d76..00000000000 --- a/ext-src/swoole_mysql_coro.cc +++ /dev/null @@ -1,2185 +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" - -// 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_connect() { - return socket && socket->is_connect(); - } - - inline int get_fd() { - return socket ? socket->get_fd() : -1; - } - - inline bool check_connection() { - if (sw_unlikely(!is_connect())) { - 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_connect() && !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=[%zu]%.*s%s", - field->name_length, - field->name, - Z_STRLEN_P(return_value), - 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_connect())) { - 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) { - if (ZVAL_IS_NULL(value)) { - *((buffer->str + null_start_offset) + (index / 8)) |= (1UL << (index % 8)); - sw_mysql_int2store((buffer->str + type_start_offset) + (index * 2), SW_MYSQL_TYPE_NULL); - } else { - 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=%llu", 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=%lld", 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_connect()); - } -} - -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_connect()); - } -} - -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_connect()); - 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 8cf59cad8e3..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); -} - -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, - 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, - 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 7e3f3692a92..a93bb50f4a1 100644 --- a/ext-src/swoole_process.cc +++ b/ext-src/swoole_process.cc @@ -10,15 +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_cxx.h" -#include "php_streams.h" -#include "php_network.h" - #include "php_swoole_process.h" + #include "swoole_server.h" #include "swoole_msg_queue.h" #include "swoole_signal.h" @@ -26,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; @@ -43,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 Worker *php_swoole_process_get_worker(zval *zobject) { +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; } @@ -65,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); @@ -106,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); @@ -124,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( @@ -327,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.aio_init) { - php_swoole_fatal_error(E_ERROR, "unable to create %s with async-io threads", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + if (SwooleTG.async_threads) { + zend_throw_error(NULL, "unable to create %s with async-io threads", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); RETURN_FALSE; } @@ -357,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 @@ -365,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); @@ -417,24 +349,18 @@ static PHP_METHOD(swoole_process, __construct) { static PHP_METHOD(swoole_process, __destruct) {} static PHP_METHOD(swoole_process, wait) { - int status; zend_bool blocking = 1; if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &blocking) == FAILURE) { RETURN_FALSE; } - int options = 0; - if (!blocking) { - options |= WNOHANG; - } - - pid_t pid = swoole_waitpid(-1, &status, options); - if (pid > 0) { + auto exit_status = swoole::wait_process(-1, blocking ? 0 : WNOHANG); + if (exit_status.get_pid() > 0) { array_init(return_value); - add_assoc_long(return_value, "pid", pid); - add_assoc_long(return_value, "code", WEXITSTATUS(status)); - add_assoc_long(return_value, "signal", WTERMSIG(status)); + add_assoc_long(return_value, "pid", exit_status.get_pid()); + add_assoc_long(return_value, "code", exit_status.get_code()); + add_assoc_long(return_value, "signal", exit_status.get_signal()); } else { RETURN_FALSE; } @@ -442,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) { @@ -468,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,7 +440,7 @@ static PHP_METHOD(swoole_process, kill) { int ret = swoole_kill((int) pid, (int) sig); if (ret < 0) { if (!(sig == 0 && errno == ESRCH)) { - php_swoole_sys_error(E_WARNING, "swKill(%d, %d) failed", (int) pid, (int) sig); + php_swoole_sys_error(E_WARNING, "kill(%d, %d) failed", (int) pid, (int) sig); } RETURN_FALSE; } @@ -522,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) @@ -542,10 +469,7 @@ static PHP_METHOD(swoole_process, signal) { RETURN_FALSE; } - php_swoole_check_reactor(); - - 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); @@ -555,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 { @@ -567,40 +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; } - // for swSignalfd_setup - SwooleTG.reactor->check_signalfd = true; + if (sw_server() && sw_server()->is_sync_process()) { + if (signal_fci_caches[signo]) { + sw_callable_free(signal_fci_caches[signo]); + } else { + SwooleTG.signal_listener_num++; + } + signal_fci_caches[signo] = fci_cache; +#ifdef SW_USE_THREAD_CONTEXT + swoole_event_defer([signo, handler](void *) { swoole_signal_set(signo, handler); }, nullptr); +#else + swoole_signal_set(signo, handler); +#endif + RETURN_TRUE; + } + + php_swoole_check_reactor(); 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; } @@ -653,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); + 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); } @@ -676,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; } @@ -722,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); @@ -735,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; } @@ -779,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) { @@ -792,7 +728,7 @@ static PHP_METHOD(swoole_process, read) { zend_string *buf = zend_string_alloc(buf_size, 0); ssize_t ret = process->pipe_current->read(buf->val, buf_size); - + if (ret < 0) { efree(buf); if (errno != EINTR) { @@ -926,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; @@ -997,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); } } } @@ -1012,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) { @@ -1064,6 +1030,10 @@ static PHP_METHOD(swoole_process, exit) { ret_code = 1; } + if (swoole_event_is_available()) { + swoole_event_free(); + } + exit(ret_code); } @@ -1155,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 a198ca4bab3..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,102 +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 sw_inline ProcessPoolObject *php_swoole_process_pool_fetch_object(zend_object *obj) { - return (ProcessPoolObject *) ((char *) obj - swoole_process_pool_handlers.offset); -} +static void process_pool_signal_handler(int sig); -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(zend_object *obj) { + return (ProcessPoolObject *) ((char *) obj - swoole_process_pool_handlers.offset); } -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 ProcessPoolObject *process_pool_fetch_object(zval *zobject) { + return process_pool_fetch_object(Z_OBJ_P(zobject)); } -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 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_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 @@ -125,152 +113,215 @@ 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); +static PHP_METHOD(swoole_process_pool, stop); 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, start, 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; + current_pool = pool; + 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)); + } } - // eventloop create - if (pp->enable_coroutine && php_swoole_reactor_init() < 0) { - return; + + 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: + current_pool->read_message = true; + break; default: break; } } +ProcessPool *sw_process_pool() { + return current_pool; +} + static PHP_METHOD(swoole_process_pool, __construct) { zval *zobject = ZEND_THIS; zend_long worker_num; @@ -280,56 +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)); - if (ProcessPool::create(pool, worker_num, (key_t) msgq_key, ipc_type) < 0) { + *pool = {}; + 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) { - if (pool->set_protocol(0, SW_INPUT_BUFFER_SIZE) < 0) { - zend_throw_exception_ex(swoole_exception_ce, errno, "failed to create process pool"); - RETURN_FALSE; - } - } - } + 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) { @@ -343,21 +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); + } + 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"); @@ -366,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 = fci_cache; - sw_zend_fci_cache_persist(pp->onStart); - RETURN_TRUE; + pp->onStart = sw_callable_create(zfn); + } else if (SW_STRCASEEQ(name, l_name, "Shutdown")) { + if (pp->onShutdown) { + sw_callable_free(pp->onShutdown); + } + 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) { @@ -431,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"); @@ -449,10 +507,10 @@ static PHP_METHOD(swoole_process_pool, listen) { int ret; // unix socket - if (SW_STRCASECT(host, l_host, "unix:/")) { - ret = pool->create_unix_socket(host + 5, backlog); + if (SW_STR_ISTARTS_WITH(host, l_host, "unix:/")) { + ret = pool->listen(host + 5, backlog); } else { - ret = pool->create_tcp_socket(host, port, backlog); + ret = pool->listen(host, port, backlog); } pool->stream_info_->socket->set_fd_option(0, 1); @@ -463,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; @@ -478,55 +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); - SwooleG.use_signalfd = 0; + 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)); + } +} +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); + // 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(); @@ -535,11 +638,16 @@ 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); } } -extern void php_swoole_process_set_worker(zval *zobject, Worker *worker); +static PHP_METHOD(swoole_process_pool, detach) { + if (current_pool == nullptr) { + RETURN_FALSE; + } + RETURN_BOOL(current_pool->detach()); +} static PHP_METHOD(swoole_process_pool, getProcess) { long worker_id = -1; @@ -556,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 = @@ -573,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; @@ -589,21 +697,52 @@ 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 { + auto _worker = php_swoole_process_get_worker(zprocess); + if (_worker->pid != current_pool->workers[worker_id].pid) { + _worker->pid = current_pool->workers[worker_id].pid; + zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(zprocess), ZEND_STRL("pid"), _worker->pid); + } } RETURN_ZVAL(zprocess, 1, 0); } +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 1dda946b464..00000000000 --- a/ext-src/swoole_redis_coro.cc +++ /dev/null @@ -1,4508 +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)) - -#define SW_REDIS_COMMAND_CHECK \ - Coroutine::get_current_safe(); \ - swRedisClient *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); \ - } - -typedef struct { - 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; -} swRedisClient; - -typedef struct { - zval _value; - zval *value; - swRedisClient *redis; -} swRedis_result; - -enum { SW_REDIS_MODE_MULTI, SW_REDIS_MODE_PIPELINE }; - -static void swoole_redis_coro_parse_result(swRedisClient *redis, zval *return_value, redisReply *reply); - -static sw_inline swRedisClient *php_swoole_redis_coro_fetch_object(zend_object *obj) { - return (swRedisClient *) ((char *) obj - swoole_redis_coro_handlers.offset); -} - -static sw_inline swRedisClient *php_swoole_get_redis_client(zval *zobject) { - swRedisClient *redis = (swRedisClient *) 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(swRedisClient *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) { - swRedisClient *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) { - swRedisClient *redis = (swRedisClient *) zend_object_alloc(sizeof(swRedisClient), 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(swRedisClient *redis, char *pw, size_t pw_len); -static bool redis_select_db(swRedisClient *redis, long db_number); -static void redis_request( - swRedisClient *redis, int argc, char **argv, size_t *argvlen, zval *return_value, bool retry = false); - -static bool swoole_redis_coro_connect(swRedisClient *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(swRedisClient *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(swRedisClient *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(swRedisClient *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( - swRedisClient *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); - } - } - } - int i; - for (i = 0; i < argc; i++) { - 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, swRedisClient, 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(swRedisClient *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) { - swRedisClient *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) { - swRedisClient *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) { - swRedisClient *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) { - swRedisClient *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) { - swRedisClient *redis = php_swoole_get_redis_client(ZEND_THIS); - - RETURN_BOOL(redis->defer); -} - -static PHP_METHOD(swoole_redis_coro, setDefer) { - swRedisClient *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) { - swRedisClient *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")); -} - -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(swRedisClient *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_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 6de5c23669f..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,88 +22,75 @@ BEGIN_EXTERN_C() #include "ext/standard/php_string.h" +#include "stubs/php_swoole_redis_server_arginfo.h" END_EXTERN_C() +using swoole::Connection; +using swoole::ListenPort; +using swoole::RecvData; +using swoole::Server; +using swoole::String; -static zend_class_entry *swoole_redis_server_ce; -static zend_object_handlers swoole_redis_server_handlers; +namespace Redis = swoole::redis; -static std::unordered_map redis_handlers; +zend_class_entry *swoole_redis_server_ce; +zend_object_handlers swoole_redis_server_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, start); static PHP_METHOD(swoole_redis_server, setHandler); static PHP_METHOD(swoole_redis_server, getHandler); static PHP_METHOD(swoole_redis_server, format); SW_EXTERN_C_END // clang-format off -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_redis_server_start, 0, 0, 0) -ZEND_END_ARG_INFO() - -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, start, arginfo_swoole_redis_server_start, ZEND_ACC_PUBLIC) - 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(); } -static int redis_onReceive(swServer *serv, swRecvData *req) { +int php_swoole_redis_server_onReceive(Server *serv, RecvData *req) { int fd = req->info.fd; - swConnection *conn = serv->get_connection_by_session_id(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; } - swListenPort *port = serv->get_port_by_fd(conn->fd); + ListenPort *port = serv->get_port_by_fd(conn->fd); // other server port if (!port->open_redis_protocol) { - return php_swoole_onReceive(serv, req); + return php_swoole_server_onReceive(serv, req); } zval zdata; @@ -113,41 +100,41 @@ static int redis_onReceive(swServer *serv, swRecvData *req) { int ret; int length = 0; - zval zparams; + 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; @@ -156,7 +143,7 @@ static int redis_onReceive(swServer *serv, swRecvData *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: @@ -172,7 +159,11 @@ static int redis_onReceive(swServer *serv, swRecvData *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()) { @@ -181,14 +172,14 @@ static int redis_onReceive(swServer *serv, swRecvData *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), @@ -206,80 +197,43 @@ static int redis_onReceive(swServer *serv, swRecvData *req) { return SW_OK; } -static PHP_METHOD(swoole_redis_server, start) { - swServer *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - zval *zserv = ZEND_THIS; - - if (serv->is_started()) { - php_swoole_error(E_WARNING, "server is running, unable to execute %s->start", SW_Z_OBJCE_NAME_VAL_P(zserv)); - RETURN_FALSE; - } - - php_swoole_server_register_callbacks(serv); - - serv->onReceive = redis_onReceive; - - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_ce, zserv, ZEND_STRL("setting"), 0); - - add_assoc_bool(zsetting, "open_http_protocol", 0); - add_assoc_bool(zsetting, "open_mqtt_protocol", 0); - add_assoc_bool(zsetting, "open_eof_check", 0); - add_assoc_bool(zsetting, "open_length_check", 0); - add_assoc_bool(zsetting, "open_redis_protocol", 0); - - auto primary_port = serv->get_primary_port(); - - primary_port->open_http_protocol = 0; - primary_port->open_mqtt_protocol = 0; - primary_port->open_eof_check = 0; - primary_port->open_length_check = 0; - primary_port->open_redis_protocol = 1; - - php_swoole_server_before_start(serv, zserv); - - if (serv->start() < 0) { - php_swoole_fatal_error(E_ERROR, "server failed to start. Error: %s", sw_error); - } - - RETURN_TRUE; -} - static PHP_METHOD(swoole_redis_server, setHandler) { char *command; 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; } @@ -288,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 5565e59db05..8bc3dd332eb 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -13,28 +13,41 @@ | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ + #include "php_swoole_cxx.h" + +#include "swoole_socket.h" #include "swoole_util.h" #include "thirdparty/php/standard/proc_open.h" -#include "thirdparty/php/curl/curl_arginfo.h" + +#ifdef SW_USE_CURL +#include "swoole_curl_interface.h" +#endif #include -#include -/* openssl */ -#ifndef OPENSSL_NO_TLS1_METHOD -#define HAVE_TLS1 1 +BEGIN_EXTERN_C() +#include "stubs/php_swoole_runtime_arginfo.h" + +#ifdef SW_USE_PGSQL +extern void swoole_pgsql_set_blocking(bool blocking); #endif -#ifndef OPENSSL_NO_TLS1_1_METHOD -#define HAVE_TLS11 1 + +#ifdef SW_USE_ODBC +extern void swoole_odbc_set_blocking(bool blocking); #endif -#ifndef OPENSSL_NO_TLS1_2_METHOD -#define HAVE_TLS12 1 + +#ifdef SW_USE_ORACLE +extern void swoole_oracle_set_blocking(bool blocking); #endif -#if OPENSSL_VERSION_NUMBER >= 0x10101000 && !defined(OPENSSL_NO_TLS1_3) -#define HAVE_TLS13 1 + +#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 #endif @@ -48,17 +61,11 @@ #define HAVE_SEC_LEVEL 1 #endif -#if PHP_VERSION_ID < 70400 -typedef size_t php_stream_size_t; -#else -typedef ssize_t php_stream_size_t; -#endif - 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); @@ -71,14 +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); - -#ifdef SW_USE_CURL -void swoole_native_curl_init(int module_number); -void swoole_native_curl_shutdown(); +#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); @@ -86,50 +96,29 @@ static int socket_flush(php_stream *stream); static int socket_close(php_stream *stream, int close_handle); 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 -{ +static php_stream_ops socket_ops { socket_write, 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; - double read_timeout; - double write_timeout; }; -static bool runtime_hook_init = false; -static int runtime_hook_flags = 0; - -static struct -{ +static struct { php_stream_transport_factory tcp; php_stream_transport_factory udp; php_stream_transport_factory _unix; @@ -147,30 +136,105 @@ 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 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 bool hook_internal_functions(const zend_function_entry *fes); + +static zend_internal_arg_info *get_arginfo(const char *name, size_t l_name) { + zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, l_name); + if (zf == nullptr) { + return nullptr; + } + 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_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)) -static zend_array *tmp_function_table = nullptr; +#define SW_HOOK_LIBRARY_FE(name, arg_info) \ + ZEND_RAW_FENTRY("swoole_hook_" #name, PHP_FN(swoole_user_func_handler), arg_info, 0) + +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" @@ -178,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); @@ -189,17 +252,31 @@ 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); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_PROC", PHPCoroutine::HOOK_PROC); SW_REGISTER_LONG_CONSTANT("SWOOLE_HOOK_CURL", PHPCoroutine::HOOK_CURL); 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_init(module_number); + swoole_native_curl_minit(module_number); #endif swoole_proc_open_init(module_number); } @@ -207,16 +284,52 @@ void php_swoole_runtime_minit(int module_number) { struct real_func { zend_function *function; zif_handler ori_handler; - zend_fcall_info_cache *fci_cache; + 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::Callable *fci_cache; zval name; }; -void php_swoole_runtime_rshutdown() { - if (!runtime_hook_init) { +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; } - runtime_hook_init = false; +#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) { @@ -226,20 +339,26 @@ 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; 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() { #ifdef SW_USE_CURL - swoole_native_curl_shutdown(); + swoole_native_curl_mshutdown(); #endif } @@ -278,24 +397,24 @@ static inline char *parse_ip_address_ex(const char *str, size_t str_len, int *po return host; } -static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count) -{ - php_swoole_netstream_data_t *abstract; - Socket *sock; +static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_t count) { + NetStream *abstract; ssize_t didwrite = -1; + std::shared_ptr sock; - abstract = (php_swoole_netstream_data_t *) stream->abstract; - if (UNEXPECTED(!abstract)) { + abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { goto _exit; } + sock = abstract->socket; - sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { - goto _exit; + if (abstract->blocking) { + didwrite = sock->send_all(buf, count); + } else { + didwrite = sock->get_socket()->send(buf, count, 0); + sock->set_err(errno); } - didwrite = sock->send_all(buf, count); - if (didwrite < 0 || (size_t) didwrite != count) { /* we do not expect the outer layer to continue to call the send syscall in a loop * and didwrite is meaningless if it failed */ @@ -311,48 +430,53 @@ static php_stream_size_t socket_write(php_stream *stream, const char *buf, size_ php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), didwrite, 0); } -_exit: -#if PHP_VERSION_ID < 70400 if (didwrite < 0) { - didwrite = 0; + if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_write_error(sock->errCode) == SW_WAIT) { + didwrite = 0; + } else { + stream->eof = 1; + } + } else if (didwrite == 0) { + stream->eof = 1; } -#endif + +_exit: 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)) { + abstract = (NetStream *) stream->abstract; + if (UNEXPECTED(!abstract || !abstract->socket)) { goto _exit; } + sock = abstract->socket; - sock = (Socket *) abstract->socket; - if (UNEXPECTED(!sock)) { - goto _exit; + if (abstract->blocking) { + nr_bytes = sock->recv(buf, count); + } else { + nr_bytes = sock->get_socket()->recv(buf, count, 0); + sock->set_err(errno); } - nr_bytes = sock->recv(buf, count); - - /** - * sock->errCode != ETIMEDOUT : Compatible with sync blocking IO - */ - stream->eof = (nr_bytes == 0 || (nr_bytes == -1 && sock->errCode != ETIMEDOUT && - sock->get_socket()->catch_error(sock->errCode) == SW_CLOSE)); if (nr_bytes > 0) { php_stream_notify_progress_increment(PHP_STREAM_CONTEXT(stream), nr_bytes, 0); } -_exit: -#if PHP_VERSION_ID < 70400 if (nr_bytes < 0) { - nr_bytes = 0; + if (sock->errCode == ETIMEDOUT || sock->get_socket()->catch_read_error(sock->errCode) == SW_WAIT) { + nr_bytes = 0; + } else { + stream->eof = 1; + } + } else if (nr_bytes == 0) { + stream->eof = 1; } -#endif + +_exit: return nr_bytes; } @@ -361,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; } @@ -398,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) { @@ -428,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) { @@ -555,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 @@ -583,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; @@ -596,7 +709,7 @@ static inline int socket_accept(php_stream *stream, Socket *sock, php_stream_xpo GC_ADDREF(stream->ctx); } } - return 0; + return SUCCESS; } } @@ -656,20 +769,108 @@ static int socket_setup_crypto(php_stream *stream, Socket *sock, php_stream_xpor return 0; } +static int socket_xport_crypto_setup(php_stream *stream) { + php_stream_xport_crypto_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + param.op = (decltype(param.op)) STREAM_XPORT_CRYPTO_OP_SETUP; + param.inputs.method = (php_stream_xport_crypt_method_t) 0; + param.inputs.session = NULL; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + return param.outputs.returncode; + } + + php_error_docref("streams.crypto", E_WARNING, "this stream does not support SSL/crypto"); + + return ret; +} + +static int socket_xport_crypto_enable(php_stream *stream, int activate) { + php_stream_xport_crypto_param param; + int ret; + + memset(¶m, 0, sizeof(param)); + param.op = (decltype(param.op)) STREAM_XPORT_CRYPTO_OP_ENABLE; + param.inputs.activate = activate; + + ret = php_stream_set_option(stream, PHP_STREAM_OPTION_CRYPTO_API, 0, ¶m); + + if (ret == PHP_STREAM_OPTION_RETURN_OK) { + return param.outputs.returncode; + } + + php_error_docref("streams.crypto", E_WARNING, "this stream does not support SSL/crypto"); + + return ret; +} + +static bool php_openssl_capture_peer_certs(php_stream *stream, Socket *sslsock) { + zval *val; + + std::string peer_cert = sslsock->ssl_get_peer_cert(); + if (peer_cert.empty()) { + return false; + } + + zval argv[1]; + ZVAL_STRINGL(&argv[0], peer_cert.c_str(), peer_cert.length()); + 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)) { + zval arr; + auto chain = sslsock->get_socket()->ssl_get_peer_cert_chain(INT_MAX); + + if (!chain.empty()) { + array_init(&arr); + for (auto &cert : chain) { + zval argv[1]; + ZVAL_STRINGL(&argv[0], cert.c_str(), cert.length()); + 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]); + } + } else { + ZVAL_NULL(&arr); + } + + php_stream_context_set_option(PHP_STREAM_CONTEXT(stream), "ssl", "peer_certificate_chain", &arr); + zval_ptr_dtor(&arr); + } + + return true; +} + static int socket_enable_crypto(php_stream *stream, Socket *sock, php_stream_xport_crypto_param *cparam STREAMS_DC) { + 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()) { + 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; } - return -1; + + 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 1; } #endif @@ -684,6 +885,12 @@ static inline int socket_xport_api(php_stream *stream, Socket *sock, php_stream_ case STREAM_XPORT_OP_CONNECT: 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)) { + xparam->outputs.returncode = -1; + } +#endif break; case STREAM_XPORT_OP_BIND: { if (sock->get_sock_domain() != AF_UNIX) { @@ -783,25 +990,18 @@ 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) { break; } abstract->blocking = (bool) value; - if (abstract->blocking) { - sock->set_timeout(abstract->read_timeout, Socket::TIMEOUT_READ); - sock->set_timeout(abstract->write_timeout, Socket::TIMEOUT_WRITE); - } else { - abstract->read_timeout = sock->get_timeout(Socket::TIMEOUT_READ); - abstract->write_timeout = sock->get_timeout(Socket::TIMEOUT_WRITE); - sock->set_timeout(0.001, Socket::TIMEOUT_READ | Socket::TIMEOUT_WRITE); - } break; case PHP_STREAM_OPTION_XPORT_API: { return socket_xport_api(stream, sock, (php_stream_xport_param *) ptrparam STREAMS_CC); @@ -816,17 +1016,17 @@ static int socket_set_option(php_stream *stream, int option, int value, void *pt array_init(&tmp); switch (SSL_version(ssl)) { -#ifdef HAVE_TLS13 +#ifdef TLS1_3_VERSION case TLS1_3_VERSION: proto_str = "TLSv1.3"; break; #endif -#ifdef HAVE_TLS12 +#ifdef TLS1_2_VERSION case TLS1_2_VERSION: proto_str = "TLSv1.2"; break; #endif -#ifdef HAVE_TLS11 +#ifdef TLS1_1_VERSION case TLS1_1_VERSION: proto_str = "TLSv1.1"; break; @@ -834,7 +1034,7 @@ static int socket_set_option(php_stream *stream, int option, int value, void *pt case TLS1_VERSION: proto_str = "TLSv1"; break; -#ifdef HAVE_SSL3 +#ifdef SSL3_VERSION case SSL3_VERSION: proto_str = "SSLv3"; break; @@ -896,6 +1096,78 @@ static int socket_set_option(php_stream *stream, int option, int value, void *pt return PHP_STREAM_OPTION_RETURN_OK; } +static bool socket_ssl_set_options(Socket *sock, php_stream_context *context) { + if (context && ZVAL_IS_ARRAY(&context->options)) { +#ifdef SW_USE_OPENSSL + zval *ztmp; + + 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); + + auto add_alias = [&zalias, options](const char *name, const char *alias) { + zval *ztmp; + if (php_swoole_array_get_value_ex(options, name, ztmp)) { + zend::array_set(&zalias, alias, strlen(alias), ztmp); + } + }; + + add_alias("peer_name", "ssl_host_name"); + add_alias("verify_peer", "ssl_verify_peer"); + add_alias("allow_self_signed", "ssl_allow_self_signed"); + add_alias("cafile", "ssl_cafile"); + add_alias("capath", "ssl_capath"); + add_alias("local_cert", "ssl_cert_file"); + add_alias("local_pk", "ssl_key_file"); + add_alias("passphrase", "ssl_passphrase"); + add_alias("verify_depth", "ssl_verify_depth"); + add_alias("disable_compression", "ssl_disable_compression"); + + bool ret = php_swoole_socket_set_ssl(sock, &zalias); + zval_dtor(&zalias); + return ret; + } +#endif + } + + 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, @@ -906,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 @@ -930,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)) { @@ -946,93 +1220,137 @@ 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; } - if (context && ZVAL_IS_ARRAY(&context->options)) { -#ifdef SW_USE_OPENSSL - zval *ztmp; + if (!socket_ssl_set_options(sock, context)) { + goto _failed; + } - if (sock->ssl_is_enable() && php_swoole_array_get_value(Z_ARRVAL_P(&context->options), "ssl", ztmp) && - ZVAL_IS_ARRAY(ztmp)) { + return stream; +} - zval zalias; - array_init(&zalias); - zend_array *options = Z_ARRVAL_P(ztmp); +static ZEND_FUNCTION(swoole_display_disabled_function) { + zend_error(E_WARNING, "%s() has been disabled for security reasons", get_active_function_name()); +} - 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); - } - }; +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); + if (rf) { + rf->function->internal_function.handler = ZEND_FN(swoole_display_disabled_function); + return true; + } - add_alias("peer_name", "ssl_host_name"); - add_alias("verify_peer", "ssl_verify_peer"); - add_alias("allow_self_signed", "ssl_allow_self_signed"); - add_alias("cafile", "ssl_cafile"); - add_alias("capath", "ssl_capath"); - add_alias("local_cert", "ssl_cert_file"); - add_alias("local_pk", "ssl_key_file"); - add_alias("passphrase", "ssl_passphrase"); - add_alias("verify_depth", "ssl_verify_depth"); - add_alias("disable_compression", "ssl_disable_compression"); + zend_function *zf = (zend_function *) zend_hash_str_find_ptr(EG(function_table), name, l_name); + if (zf == nullptr) { + return false; + } - php_swoole_socket_set_ssl(sock, &zalias); - if (!sock->ssl_check_context()) { - goto _failed; - } - zend_array_destroy(Z_ARRVAL(zalias)); - } -#endif + rf = (real_func *) emalloc(sizeof(real_func)); + sw_memset_zero(rf, sizeof(*rf)); + rf->function = zf; + rf->ori_handler = zf->internal_function.handler; + rf->ori_arg_info = zf->internal_function.arg_info; + rf->ori_fn_flags = zf->internal_function.fn_flags; + rf->ori_num_args = zf->internal_function.num_args; + + zf->internal_function.handler = ZEND_FN(swoole_display_disabled_function); + zf->internal_function.arg_info = nullptr; + zf->internal_function.fn_flags &= ~(ZEND_ACC_VARIADIC | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_HAS_RETURN_TYPE); + zf->internal_function.num_args = 0; + + zend_hash_add_ptr(tmp_function_table, zf->common.function_name, rf); + return true; +} + +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); + if (!rf) { + return false; } - return stream; + rf->function->internal_function.handler = rf->ori_handler; + rf->function->internal_function.arg_info = rf->ori_arg_info; + rf->function->internal_function.fn_flags = rf->ori_fn_flags; + rf->function->internal_function.num_args = rf->ori_num_args; + + 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) { -#if PHP_VERSION_ID < 80000 - zend_disable_function((char *) f.c_str(), f.length()); -#else - zend_disable_functions(f.c_str()); -#endif + disable_func(f.c_str(), f.length()); } } -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)); +void PHPCoroutine::enable_unsafe_function() { + for (auto &f : unsafe_functions) { + enable_func(f.c_str(), f.length()); + } +} - tmp_function_table = (zend_array *) emalloc(sizeof(zend_array)); - zend_hash_init(tmp_function_table, 8, nullptr, nullptr, 0); +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); +} - runtime_hook_init = true; +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; } - // php_stream + 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 { @@ -1044,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 { @@ -1055,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 { @@ -1066,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 { @@ -1077,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 { @@ -1092,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 { @@ -1103,6 +1426,81 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { } } } + *flags_ptr = flags; +} + +static void hook_stream_ops(uint32_t flags) { + // file + if (flags & PHPCoroutine::HOOK_FILE) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_FILE)) { + memcpy((void *) &php_plain_files_wrapper, &sw_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_FILE) { + memcpy((void *) &php_plain_files_wrapper, &ori_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); + } + } + // stdio + if (flags & PHPCoroutine::HOOK_STDIO) { + if (!(runtime_hook_flags & PHPCoroutine::HOOK_STDIO)) { + memcpy((void *) &php_stream_stdio_ops, &sw_php_stream_stdio_ops, sizeof(php_stream_stdio_ops)); + } + } else { + if (runtime_hook_flags & PHPCoroutine::HOOK_STDIO) { + 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); @@ -1114,16 +1512,6 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(stream_socket_pair); } } - // file - if (flags & PHPCoroutine::HOOK_FILE) { - if (!(runtime_hook_flags & PHPCoroutine::HOOK_FILE)) { - memcpy((void *) &php_plain_files_wrapper, &sw_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); - } - } else { - if (runtime_hook_flags & PHPCoroutine::HOOK_FILE) { - memcpy((void *) &php_plain_files_wrapper, &ori_php_plain_files_wrapper, sizeof(php_plain_files_wrapper)); - } - } // sleep if (flags & PHPCoroutine::HOOK_SLEEP) { if (!(runtime_hook_flags & PHPCoroutine::HOOK_SLEEP)) { @@ -1160,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) { @@ -1170,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) { @@ -1225,33 +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)) { -#if PHP_VERSION_ID >= 80000 - hook_internal_functions(swoole_native_curl_functions); -#else - hook_func(ZEND_STRL("curl_close"), PHP_FN(swoole_native_curl_close)); - hook_func(ZEND_STRL("curl_copy_handle"), PHP_FN(swoole_native_curl_copy_handle)); - hook_func(ZEND_STRL("curl_errno"), PHP_FN(swoole_native_curl_errno)); - hook_func(ZEND_STRL("curl_error"), PHP_FN(swoole_native_curl_error)); - hook_func(ZEND_STRL("curl_exec"), PHP_FN(swoole_native_curl_exec)); - hook_func(ZEND_STRL("curl_getinfo"), PHP_FN(swoole_native_curl_getinfo)); - hook_func(ZEND_STRL("curl_init"), PHP_FN(swoole_native_curl_init)); - hook_func(ZEND_STRL("curl_setopt"), PHP_FN(swoole_native_curl_setopt)); - hook_func(ZEND_STRL("curl_setopt_array"), PHP_FN(swoole_native_curl_setopt_array)); - hook_func(ZEND_STRL("curl_reset"), PHP_FN(swoole_native_curl_reset)); - hook_func(ZEND_STRL("curl_pause"), PHP_FN(swoole_native_curl_pause)); - hook_func(ZEND_STRL("curl_escape"), PHP_FN(swoole_native_curl_escape)); - hook_func(ZEND_STRL("curl_unescape"), PHP_FN(swoole_native_curl_unescape)); - #endif + 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) { @@ -1268,22 +1671,35 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { SW_UNHOOK_FUNC(curl_pause); SW_UNHOOK_FUNC(curl_escape); SW_UNHOOK_FUNC(curl_unescape); + + SW_UNHOOK_FUNC(curl_multi_init); + SW_UNHOOK_FUNC(curl_multi_add_handle); + SW_UNHOOK_FUNC(curl_multi_exec); + SW_UNHOOK_FUNC(curl_multi_errno); + SW_UNHOOK_FUNC(curl_multi_select); + SW_UNHOOK_FUNC(curl_multi_setopt); + SW_UNHOOK_FUNC(curl_multi_getcontent); + SW_UNHOOK_FUNC(curl_multi_info_read); + SW_UNHOOK_FUNC(curl_multi_remove_handle); + SW_UNHOOK_FUNC(curl_multi_close); } } #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) { @@ -1297,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; } @@ -1313,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) { @@ -1381,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)); } } @@ -1516,9 +1940,8 @@ static int stream_array_emulate_read_fd_set(zval *stream_array) { return 0; } - ZVAL_NEW_ARR(&new_array); + array_init_size(&new_array, zend_hash_num_elements(Z_ARRVAL_P(stream_array))); ht = Z_ARRVAL(new_array); - zend_hash_init(ht, zend_hash_num_elements(Z_ARRVAL_P(stream_array)), nullptr, ZVAL_PTR_DTOR, 0); ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(stream_array), num_ind, key, elem) { ZVAL_DEREF(elem); @@ -1558,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) { @@ -1588,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 @@ -1649,17 +2095,11 @@ static PHP_FUNCTION(swoole_stream_select) { } retval++; } - delete kv; } RETURN_LONG(retval); } -bool hook_internal_functions(const zend_function_entry *fes) { - zend_unregister_functions(fes, -1, CG(function_table)); - return zend_register_functions(NULL, fes, NULL, MODULE_PERSISTENT) == SUCCESS; -} - static void hook_func(const char *name, size_t l_name, zif_handler handler, zend_internal_arg_info *arg_info) { real_func *rf = (real_func *) zend_hash_str_find_ptr(tmp_function_table, name, l_name); bool use_php_func = false; @@ -1672,6 +2112,9 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend } if (rf) { rf->function->internal_function.handler = handler; + if (arg_info) { + rf->function->internal_function.arg_info = arg_info; + } return; } @@ -1680,30 +2123,41 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend return; } + 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 = 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); + memcpy(func + 7, fn_str->val, fn_str->len); - ZVAL_STRINGL(&rf->name, func, zf->common.function_name->len + 7); - - 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) { @@ -1711,27 +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; } @@ -1739,6 +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; @@ -1772,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 - 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); +#ifdef HAVE_TZSET + if (zend_binary_strcasecmp(key.c_str(), key.length(), ZEND_STRL("TZ")) == 0) { + tzset(); + } +#endif + result = true; + } else { + result = false; + } + + tsrm_env_unlock(); + RETURN_BOOL(result); } +/* }}} */ +#endif diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index c9d7798c518..da8d55951b9 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -10,12 +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" @@ -25,30 +28,13 @@ #include #endif -#include -#include -#include +BEGIN_EXTERN_C() +#include "ext/json/php_json.h" +#include "stubs/php_swoole_server_arginfo.h" +END_EXTERN_C() using namespace swoole; -struct TaskCo; - -struct ServerProperty { - std::vector ports; - std::vector user_processes; - php_swoole_server_port_property *primary_port; - zend_fcall_info_cache *callbacks[PHP_SWOOLE_SERVER_CALLBACK_NUM]; - std::unordered_map task_callbacks; - std::unordered_map task_coroutine_map; - std::unordered_map *> send_coroutine_map; -}; - -struct ServerObject { - Server *serv; - ServerProperty *property; - zend_object std; -}; - struct ConnectionIterator { int current_fd; SessionId session_id; @@ -57,15 +43,6 @@ struct ConnectionIterator { int index; }; -struct TaskCo { - FutureTask context; - int *list; - uint32_t count; - zval *result; - TimerNode *timer; - ServerObject *server_object; -}; - struct ServerEvent { enum php_swoole_server_callback_type type; std::string name; @@ -74,85 +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_onWorkerStart(Server *, int worker_id); -static void php_swoole_onBeforeReload(Server *serv); -static void php_swoole_onAfterReload(Server *serv); -static void php_swoole_onWorkerStop(Server *, int worker_id); -static void php_swoole_onWorkerExit(Server *serv, int worker_id); -static void php_swoole_onUserWorkerStart(Server *serv, Worker *worker); -static int php_swoole_onTask(Server *, EventData *task); -static int php_swoole_onFinish(Server *, EventData *task); -static void php_swoole_onWorkerError(Server *serv, int worker_id, pid_t worker_pid, int status); -static void php_swoole_onManagerStart(Server *serv); -static void php_swoole_onManagerStop(Server *serv); - -static void php_swoole_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); +// 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 *, Worker *worker); +static void php_swoole_server_onWorkerExit(Server *serv, Worker *worker); +static void php_swoole_server_onUserWorkerStart(Server *serv, Worker *worker); +static int php_swoole_server_onTask(Server *, EventData *task); +static int php_swoole_server_onFinish(Server *, EventData *task); +static void php_swoole_server_onPipeMessage(Server *serv, EventData *req); +static void php_swoole_server_onWorkerError(Server *serv, Worker *worker, const ExitStatus &exit_status); +static void php_swoole_server_onManagerStart(Server *serv); +static void php_swoole_server_onManagerStop(Server *serv); + +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); -/** - * Worker Buffer - */ -static void **php_swoole_server_worker_create_buffers(Server *serv, uint buffer_num); -static void php_swoole_server_worker_free_buffers(Server *serv, uint buffer_num, void **buffers); -static void *php_swoole_server_worker_get_buffer(Server *serv, DataHead *info); -static size_t php_swoole_server_worker_get_buffer_len(Server *serv, DataHead *info); -static void php_swoole_server_worker_add_buffer_len(Server *serv, DataHead *info, size_t len); -static void php_swoole_server_worker_move_buffer(Server *serv, PipeBuffer *buffer); -static size_t php_swoole_server_worker_get_packet(Server *serv, EventData *req, char **data_ptr); - -static inline zend_bool php_swoole_server_isset_callback(ServerObject *server_object, - ListenPort *port, - int event_type) { - php_swoole_server_port_property *property = (php_swoole_server_port_property *) port->ptr; - if (property->callbacks[event_type] || server_object->property->primary_port->callbacks[event_type]) { - return true; - } else { - return false; - } -} - 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 - PG(last_error_file) ? PG(last_error_file) : "-", + php_swoole_get_last_error_message(), + php_swoole_get_last_error_file(), PG(last_error_lineno)); } else { swoole_error_log( @@ -185,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); } @@ -196,11 +160,43 @@ static sw_inline Server *server_get_ptr(zval *zobject) { Server *php_swoole_server_get_and_check_server(zval *zobject) { Server *serv = server_get_ptr(zobject); if (UNEXPECTED(!serv)) { - php_swoole_fatal_error(E_ERROR, "Invaild instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + php_swoole_fatal_error(E_ERROR, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(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(php_swoole_server_zval_ptr(serv))); + return server_object->isset_callback(port, event_type); +} + static sw_inline void server_set_ptr(zval *zobject, Server *serv) { server_fetch_object(Z_OBJ_P(zobject))->serv = serv; } @@ -212,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; } } @@ -235,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; } } @@ -268,7 +269,7 @@ static sw_inline ConnectionIterator *php_swoole_connection_iterator_get_ptr(zval ConnectionIterator *php_swoole_connection_iterator_get_and_check_ptr(zval *zobject) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(zobject); if (UNEXPECTED(!iterator->serv)) { - php_swoole_fatal_error(E_ERROR, "Invaild instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + php_swoole_fatal_error(E_ERROR, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); } return iterator; } @@ -299,7 +300,7 @@ static sw_inline ServerTaskObject *php_swoole_server_task_fetch_object(zend_obje static sw_inline Server *php_swoole_server_task_get_server(zval *zobject) { Server *serv = php_swoole_server_task_fetch_object(Z_OBJ_P(zobject))->serv; if (!serv) { - php_swoole_fatal_error(E_ERROR, "Invaild instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + php_swoole_fatal_error(E_ERROR, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); } return serv; } @@ -311,7 +312,7 @@ static sw_inline void php_swoole_server_task_set_server(zval *zobject, Server *s static sw_inline DataHead *php_swoole_server_task_get_info(zval *zobject) { ServerTaskObject *task = php_swoole_server_task_fetch_object(Z_OBJ_P(zobject)); if (!task->serv) { - php_swoole_fatal_error(E_ERROR, "Invaild instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + php_swoole_fatal_error(E_ERROR, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); } return &task->info; } @@ -332,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); @@ -514,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); @@ -535,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); @@ -567,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, @@ -694,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------------------------------------- @@ -710,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, @@ -729,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); @@ -747,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); @@ -755,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); @@ -774,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); - php_swoole_server_port_property *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 = (php_swoole_server_port_property *) 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]; } } @@ -797,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); @@ -828,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; } @@ -845,23 +709,23 @@ void php_swoole_get_recv_data(Server *serv, zval *zdata, RecvData *req) { ZVAL_EMPTY_STRING(zdata); } else { if (req->info.flags & SW_EVENT_DATA_OBJ_PTR) { - zend_string *worker_buffer = zend::fetch_zend_string_by_val((char *) data); - ZVAL_STR(zdata, worker_buffer); + zend::assign_zend_string_by_val(zdata, (char *) data, length); + 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); - sw_set_zend_string(zdata, recv_buffer->pop(serv->recv_buffer_size), length); + zend::assign_zend_string_by_val(zdata, recv_buffer->pop(serv->recv_buffer_size), length); } else { ZVAL_STRINGL(zdata, data, length); } } } -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; } - if (UNEXPECTED(dst_worker_id >= serv->task_worker_num)) { + if (UNEXPECTED(dst_worker_id > 0 && dst_worker_id >= serv->task_worker_num)) { php_swoole_fatal_error(E_WARNING, "worker_id must be less than task_worker_num[%u]", serv->task_worker_num); return SW_ERR; } @@ -872,116 +736,41 @@ 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); extern void php_swoole_server_port_set_ptr(zval *zobject, ListenPort *port); -extern php_swoole_server_port_property *php_swoole_server_port_get_property(zval *zobject); +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); @@ -989,20 +778,21 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort php_swoole_server_port_set_ptr(zport, port); /* port property */ - php_swoole_server_port_property *property = php_swoole_server_port_get_property(zport); + ServerPortProperty *property = php_swoole_server_port_get_property(zport); property->serv = serv; 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->host.c_str()); - zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("port"), port->port); - zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("type"), port->type); - zend_update_property_long(swoole_server_port_ce, SW_Z8_OBJ_P(zport), ZEND_STRL("sock"), port->socket->fd); + 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); @@ -1023,7 +813,18 @@ static zval *php_swoole_server_add_port(ServerObject *server_object, ListenPort return zport; } -void php_swoole_server_before_start(Server *serv, zval *zobject) { +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 */ @@ -1032,14 +833,16 @@ void php_swoole_server_before_start(Server *serv, zval *zobject) { return; } -#ifdef SW_LOG_TRACE_OPEN + zval *zobject = php_swoole_server_zval_ptr(serv); auto primary_port = serv->get_primary_port(); - 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); + +#ifdef SW_LOG_TRACE_OPEN + 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) { @@ -1048,31 +851,22 @@ void php_swoole_server_before_start(Server *serv, zval *zobject) { if (serv->send_yield) { if (serv->onClose == nullptr && serv->is_support_unsafe_events()) { - serv->onClose = php_swoole_onClose; + serv->onClose = php_swoole_server_onClose; } } - /** - * init method - */ - serv->create_buffers = php_swoole_server_worker_create_buffers; - serv->free_buffers = php_swoole_server_worker_free_buffers; - serv->get_buffer = php_swoole_server_worker_get_buffer; - serv->get_buffer_len = php_swoole_server_worker_get_buffer_len; - serv->add_buffer_len = php_swoole_server_worker_add_buffer_len; - serv->move_buffer = php_swoole_server_worker_move_buffer; - serv->get_packet = php_swoole_server_worker_get_packet; + 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(); } /** * Master Process ID */ - zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("master_pid"), getpid()); + zend_update_property_long(get_ce(), SW_Z8_OBJ_P(zobject), ZEND_STRL("master_pid"), getpid()); - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_ce, zobject, ZEND_STRL("setting"), 0); + zval *zsetting = sw_zend_read_and_convert_property_array(get_ce(), zobject, ZEND_STRL("setting"), 0); if (!zend_hash_str_exists(Z_ARRVAL_P(zsetting), ZEND_STRL("worker_num"))) { add_assoc_long(zsetting, "worker_num", serv->worker_num); @@ -1086,17 +880,69 @@ void php_swoole_server_before_start(Server *serv, zval *zobject) { 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); + } - uint32_t i; - zval *zport; - zval *zport_setting; - ListenPort *port; bool find_http_port = false; - ServerObject *server_object = server_fetch_object(Z_OBJ_P(zobject)); + if (is_redis_server()) { + add_assoc_bool(zsetting, "open_redis_protocol", 1); + add_assoc_bool(zsetting, "open_http_protocol", 0); + add_assoc_bool(zsetting, "open_mqtt_protocol", 0); + add_assoc_bool(zsetting, "open_eof_check", 0); + add_assoc_bool(zsetting, "open_length_check", 0); + primary_port->clear_protocol(); + primary_port->open_redis_protocol = 1; + serv->onReceive = php_swoole_redis_server_onReceive; + } else if (is_http_server()) { + if (is_websocket_server()) { + if (!isset_callback(primary_port, SW_SERVER_CB_onMessage)) { + php_swoole_fatal_error(E_ERROR, "require onMessage callback"); + return; + } + } else if (!isset_callback(primary_port, SW_SERVER_CB_onRequest)) { + php_swoole_fatal_error(E_ERROR, "require onRequest callback"); + return; + } + + add_assoc_bool(zsetting, "open_http_protocol", 1); + add_assoc_bool(zsetting, "open_mqtt_protocol", 0); + add_assoc_bool(zsetting, "open_eof_check", 0); + add_assoc_bool(zsetting, "open_length_check", 0); + + enum protocol_flags { SW_HTTP2_PROTOCOL = 1u << 1, SW_WEBSOCKET_PROTOCOL = 1u << 2 }; + uint8_t protocol_flag = 0; + if (primary_port->open_http2_protocol) { + add_assoc_bool(zsetting, "open_http2_protocol", 1); + protocol_flag |= SW_HTTP2_PROTOCOL; + } + if (primary_port->open_websocket_protocol || is_websocket_server()) { + add_assoc_bool(zsetting, "open_websocket_protocol", 1); + protocol_flag |= SW_WEBSOCKET_PROTOCOL; + } + primary_port->clear_protocol(); + primary_port->open_http_protocol = 1; + primary_port->open_http2_protocol = !!(protocol_flag & SW_HTTP2_PROTOCOL); + primary_port->open_websocket_protocol = !!(protocol_flag & SW_WEBSOCKET_PROTOCOL); + find_http_port = true; + serv->onReceive = php_swoole_http_server_onReceive; + } else { + if (serv->if_require_packet_callback(primary_port, isset_callback(primary_port, SW_SERVER_CB_onPacket))) { + php_swoole_fatal_error(E_ERROR, "require onPacket callback"); + return; + } + if (serv->if_require_receive_callback(primary_port, isset_callback(primary_port, SW_SERVER_CB_onReceive))) { + php_swoole_fatal_error(E_ERROR, "require onReceive callback"); + return; + } + serv->onReceive = php_swoole_server_onReceive; + } - for (i = 1; i < server_object->property->ports.size(); i++) { - zport = server_object->property->ports.at(i); - zport_setting = sw_zend_read_property_ex(swoole_server_port_ce, zport, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); + for (size_t i = 1; i < property->ports.size(); i++) { + zval *zport = property->ports.at(i); + zval *zport_setting = + sw_zend_read_property_ex(swoole_server_port_ce, zport, SW_ZSTR_KNOWN(SW_ZEND_STR_SETTING), 0); // use swoole_server->setting if (zport_setting == nullptr || ZVAL_IS_NULL(zport_setting)) { Z_TRY_ADDREF_P(zport); @@ -1104,12 +950,11 @@ void php_swoole_server_before_start(Server *serv, zval *zobject) { } } - for (i = 0; i < server_object->property->ports.size(); i++) { - zport = server_object->property->ports.at(i); - port = php_swoole_server_port_get_and_check_ptr(zport); + for (size_t i = 0; i < property->ports.size(); i++) { + zval *zport = property->ports.at(i); + ListenPort *port = php_swoole_server_port_get_and_check_ptr(zport); - if (serv->if_require_packet_callback( - port, php_swoole_server_isset_callback(server_object, port, SW_SERVER_CB_onPacket))) { + if (serv->if_require_packet_callback(port, isset_callback(port, SW_SERVER_CB_onPacket))) { php_swoole_fatal_error(E_ERROR, "require onPacket callback"); return; } @@ -1124,8 +969,8 @@ void php_swoole_server_before_start(Server *serv, zval *zobject) { 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; } @@ -1135,17 +980,24 @@ void php_swoole_server_before_start(Server *serv, zval *zobject) { if (port->open_http_protocol) { find_http_port = true; if (port->open_websocket_protocol) { - if (!php_swoole_server_isset_callback(server_object, port, SW_SERVER_CB_onMessage)) { + if (!isset_callback(port, SW_SERVER_CB_onMessage) && !isset_callback(port, SW_SERVER_CB_onReceive)) { php_swoole_fatal_error(E_ERROR, "require onMessage callback"); return; } - } else if (port->open_http_protocol && - !php_swoole_server_isset_callback(server_object, port, SW_SERVER_CB_onRequest)) { + } else if (port->open_http_protocol && !isset_callback(port, SW_SERVER_CB_onRequest) && + !isset_callback(port, SW_SERVER_CB_onReceive)) { php_swoole_fatal_error(E_ERROR, "require onRequest callback"); return; } + if (!is_http_server() && isset_callback(port, SW_SERVER_CB_onRequest)) { + php_swoole_error( + E_WARNING, + "use %s class and open http related protocols may lead to some errors (inconsistent class type)", + SW_Z_OBJCE_NAME_VAL_P(zobject)); + } } else if (!port->open_redis_protocol) { - if (port->is_stream() && !php_swoole_server_isset_callback(server_object, port, SW_SERVER_CB_onReceive)) { + // redis server does not require to set receive callback + if (port->is_stream() && !isset_callback(port, SW_SERVER_CB_onReceive)) { php_swoole_fatal_error(E_ERROR, "require onReceive callback"); return; } @@ -1153,77 +1005,56 @@ void php_swoole_server_before_start(Server *serv, zval *zobject) { } if (find_http_port) { - serv->onReceive = php_swoole_http_onReceive; - if (serv->is_support_unsafe_events()) { - serv->onClose = php_swoole_http_onClose; + serv->onReceive = php_swoole_http_server_onReceive; + if (serv->is_base_mode()) { + serv->onClose = php_swoole_http_server_onClose; } - if (!instanceof_function(Z_OBJCE_P(zobject), swoole_http_server_ce)) { - php_swoole_error( - E_WARNING, - "use %s class and open http related protocols may lead to some errors (inconsistent class type)", - SW_Z_OBJCE_NAME_VAL_P(zobject)); - } - php_swoole_http_server_init_global_variant(); } -} -void php_swoole_server_register_callbacks(Server *serv) { - ServerObject *server_object = server_fetch_object(Z_OBJ_P((zval *) serv->private_data_2)); - /* - * optional callback - */ - if (server_object->property->callbacks[SW_SERVER_CB_onStart] != nullptr) { - serv->onStart = php_swoole_onStart; +#ifdef SW_THREAD + if (serv->is_thread_mode()) { + copy_setting(zsetting); } - serv->onShutdown = php_swoole_onShutdown; - /** - * require callback, set the master/manager/worker PID - */ - serv->onWorkerStart = php_swoole_onWorkerStart; +#endif - if (server_object->property->callbacks[SW_SERVER_CB_onBeforeReload] != nullptr) { - serv->onBeforeReload = php_swoole_onBeforeReload; + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onBeforeStart", 1, zobject); } +} - if (server_object->property->callbacks[SW_SERVER_CB_onAfterReload] != nullptr) { - serv->onAfterReload = php_swoole_onAfterReload; - } +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; + 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; - if (server_object->property->callbacks[SW_SERVER_CB_onWorkerStop] != nullptr) { - serv->onWorkerStop = php_swoole_onWorkerStop; - } - serv->onWorkerExit = php_swoole_onWorkerExit; - /** - * Task Worker - */ - if (server_object->property->callbacks[SW_SERVER_CB_onTask] != nullptr) { - serv->onTask = php_swoole_onTask; - serv->onFinish = php_swoole_onFinish; - } - if (server_object->property->callbacks[SW_SERVER_CB_onWorkerError] != nullptr) { - serv->onWorkerError = php_swoole_onWorkerError; - } - if (server_object->property->callbacks[SW_SERVER_CB_onManagerStart] != nullptr) { - serv->onManagerStart = php_swoole_onManagerStart; + // data plane + if (property->callbacks[SW_SERVER_CB_onTask] != nullptr) { + serv->onTask = php_swoole_server_onTask; + serv->onFinish = php_swoole_server_onFinish; } - if (server_object->property->callbacks[SW_SERVER_CB_onManagerStop] != nullptr) { - serv->onManagerStop = php_swoole_onManagerStop; - } - if (server_object->property->callbacks[SW_SERVER_CB_onPipeMessage] != nullptr) { - serv->onPipeMessage = php_swoole_onPipeMessage; + if (property->callbacks[SW_SERVER_CB_onPipeMessage] != nullptr) { + serv->onPipeMessage = php_swoole_server_onPipeMessage; } if (serv->send_yield && serv->is_support_unsafe_events()) { - serv->onBufferEmpty = php_swoole_onBufferEmpty; + 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) { @@ -1241,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=%d|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; @@ -1271,35 +1102,38 @@ static void php_swoole_onPipeMessage(Server *serv, EventData *req) { if (serv->event_object) { zval *object = &args[1]; object_init_ex(object, swoole_server_pipe_message_ce); - 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_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_long(swoole_server_pipe_message_ce, + SW_Z8_OBJ_P(object), + ZEND_STRL("worker_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"), 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_onReceive(Server *serv, RecvData *req) { - zend_fcall_info_cache *fci_cache = - php_swoole_server_get_fci_cache(serv, req->info.server_fd, SW_SERVER_CB_onReceive); +int php_swoole_server_onReceive(Server *serv, RecvData *req) { + 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; @@ -1309,12 +1143,12 @@ int php_swoole_onReceive(Server *serv, RecvData *req) { zval *object = &args[1]; zval data; 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) req->info.fd); - zend_update_property_long(swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("reactor_id"), - (zend_long) req->info.reactor_id); - zend_update_property_double(swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), - req->info.time); + zend_update_property_long( + swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("fd"), (zend_long) req->info.fd); + zend_update_property_long( + swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("reactor_id"), (zend_long) req->info.reactor_id); + zend_update_property_double( + swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("dispatch_time"), req->info.time); php_swoole_get_recv_data(serv, &data, req); zend_update_property(swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("data"), &data); zval_ptr_dtor(&data); @@ -1326,7 +1160,7 @@ int php_swoole_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,46 +1174,52 @@ int php_swoole_onReceive(Server *serv, RecvData *req) { return SW_OK; } -int php_swoole_onPacket(Server *serv, RecvData *req) { - zval *zserv = (zval *) serv->private_data_2; +int php_swoole_server_onPacket(Server *serv, RecvData *req) { + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; args[0] = *zserv; - DgramPacket *packet = (DgramPacket*) req->data; + DgramPacket *packet = (DgramPacket *) req->data; if (serv->event_object) { zval zobject; object_init_ex(&zobject, swoole_server_packet_ce); - zend_update_property_long(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("server_socket"), - req->info.server_fd); - zend_update_property_double(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("dispatch_time"), - req->info.time); + zend_update_property_long( + swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("server_socket"), req->info.server_fd); + zend_update_property_double( + swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("dispatch_time"), req->info.time); Connection *server_sock = serv->get_connection(req->info.server_fd); if (server_sock) { - zend_update_property_long(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("server_port"), - server_sock->info.get_port()); + zend_update_property_long( + swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("server_port"), server_sock->info.get_port()); } char address[INET6_ADDRSTRLEN]; if (packet->socket_type == SW_SOCK_UDP) { inet_ntop(AF_INET, &packet->socket_addr.addr.inet_v4.sin_addr, address, sizeof(address)); zend_update_property_string(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("address"), address); - zend_update_property_long(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("port"), + zend_update_property_long(swoole_server_packet_ce, + SW_Z8_OBJ_P(&zobject), + ZEND_STRL("port"), ntohs(packet->socket_addr.addr.inet_v4.sin_port)); } else if (packet->socket_type == SW_SOCK_UDP6) { inet_ntop(AF_INET6, &packet->socket_addr.addr.inet_v6.sin6_addr, address, sizeof(address)); zend_update_property_string(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("address"), address); - zend_update_property_long(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("port"), + zend_update_property_long(swoole_server_packet_ce, + SW_Z8_OBJ_P(&zobject), + ZEND_STRL("port"), ntohs(packet->socket_addr.addr.inet_v6.sin6_port)); } else if (packet->socket_type == SW_SOCK_UNIX_DGRAM) { - zend_update_property_string(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("address"), + zend_update_property_string(swoole_server_packet_ce, + SW_Z8_OBJ_P(&zobject), + ZEND_STRL("address"), packet->socket_addr.addr.un.sun_path); } - zend_update_property_stringl(swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("data"), packet->data, - packet->length); + zend_update_property_stringl( + swoole_server_packet_ce, SW_Z8_OBJ_P(&zobject), ZEND_STRL("data"), packet->data, packet->length); args[1] = zobject; argc = 2; } else { @@ -1410,9 +1250,8 @@ int php_swoole_onPacket(Server *serv, RecvData *req) { argc = 3; } - zend_fcall_info_cache *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)); } @@ -1429,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_onTask(Server *serv, EventData *req) { +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; } @@ -1456,16 +1298,16 @@ static int php_swoole_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, @@ -1476,53 +1318,42 @@ static int php_swoole_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); } return SW_OK; } -static int php_swoole_onFinish(Server *serv, EventData *req) { - zval *zserv = (zval *) serv->private_data_2; +static int php_swoole_server_onFinish(Server *serv, EventData *req) { + 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; @@ -1530,39 +1361,32 @@ static int php_swoole_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; } @@ -1573,28 +1397,29 @@ static int php_swoole_onFinish(Server *serv, EventData *req) { if (serv->event_object) { 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); - 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_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_long( + 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) 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"), 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]); } @@ -1602,172 +1427,203 @@ static int php_swoole_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)); - zend_fcall_info_cache *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_onManagerStart(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; +static void php_swoole_server_onManagerStart(Server *serv) { + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - zend_fcall_info_cache *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_onManagerStop(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; +static void php_swoole_server_onManagerStop(Server *serv) { + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - zend_fcall_info_cache *fci_cache = server_object->property->callbacks[SW_SERVER_CB_onManagerStop]; + auto cb = server_object->get_callback(SW_SERVER_CB_onManagerStop); - if (UNEXPECTED(!zend::function::call(fci_cache, 1, zserv, nullptr, false))) { + if (SWOOLE_G(enable_library)) { + zend::function::call("\\Swoole\\Server\\Helper::onManagerStop", 1, zserv); + } + + 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)); - zend_fcall_info_cache *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_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)); - zend_fcall_info_cache *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_onBeforeReload(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; +static void php_swoole_server_onBeforeReload(Server *serv) { + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - zend_fcall_info_cache *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_onAfterReload(Server *serv) { - zval *zserv = (zval *) serv->private_data_2; +static void php_swoole_server_onAfterReload(Server *serv) { + zval *zserv = php_swoole_server_zval_ptr(serv); ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); - zend_fcall_info_cache *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_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)); - zend_fcall_info_cache *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_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)); - zend_fcall_info_cache *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); - zval *zserv = (zval *) serv->private_data_2; + 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; + } + + 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_onWorkerError(Server *serv, int worker_id, pid_t worker_pid, int 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)); - zend_fcall_info_cache *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; @@ -1776,21 +1632,30 @@ static void php_swoole_onWorkerError(Server *serv, int worker_id, pid_t worker_p 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_pid"), worker_pid); - zend_update_property_long(swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("status"), status); - zend_update_property_long(swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("exit_code"), WEXITSTATUS(status)); - zend_update_property_long(swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("signal"), WTERMSIG(status)); + 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( + swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("status"), exit_status.get_status()); + zend_update_property_long( + swoole_server_status_info_ce, SW_Z8_OBJ_P(object), ZEND_STRL("exit_code"), exit_status.get_code()); + zend_update_property_long( + 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[2], worker_pid); - ZVAL_LONG(&args[3], WEXITSTATUS(status)); - ZVAL_LONG(&args[4], WTERMSIG(status)); + 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)); } @@ -1799,64 +1664,75 @@ static void php_swoole_onWorkerError(Server *serv, int worker_id, pid_t worker_p } } -void php_swoole_onConnect(Server *serv, DataHead *info) { - zend_fcall_info_cache *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; +void php_swoole_server_onConnect(Server *serv, DataHead *info) { + 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_onClose(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; +void php_swoole_server_onClose(Server *serv, DataHead *info) { + 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(info->fd); - if (_i_coros_list != server_object->property->send_coroutine_map.end()) { - auto coros_list = _i_coros_list->second; - server_object->property->send_coroutine_map.erase(info->fd); - while (!coros_list->empty()) { - FutureTask *context = coros_list->front(); - coros_list->pop_front(); + 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 (!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, info->fd); + co->resume(); } - delete coros_list; + delete co_list; } } - zend_fcall_info_cache *fci_cache = php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onClose); - if (fci_cache) { - zval *zserv = (zval *) serv->private_data_2; + 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; + } + if (conn->websocket_status != swoole::websocket::STATUS_ACTIVE) { + 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)) { + cb = php_swoole_server_get_callback(serv, info->server_fd, SW_SERVER_CB_onDisconnect); + } + } + if (cb) { + zval *zserv = php_swoole_server_zval_ptr(serv); zval args[3]; int argc; args[0] = *zserv; @@ -1864,20 +1740,20 @@ void php_swoole_onClose(Server *serv, DataHead *info) { 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); + zend_update_property_long( + swoole_server_event_ce, SW_Z8_OBJ_P(object), ZEND_STRL("fd"), (zend_long) session_id); + 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[1], session_id); ZVAL_LONG(&args[2], info->reactor_id); 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)); } @@ -1885,132 +1761,78 @@ void php_swoole_onClose(Server *serv, DataHead *info) { zval_ptr_dtor(&args[1]); } } + if (conn->http2_stream) { + swoole_http2_server_session_free(conn); + } } -void php_swoole_onBufferFull(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; - zend_fcall_info_cache *fci_cache = - php_swoole_server_get_fci_cache(serv, info->server_fd, SW_SERVER_CB_onBufferFull); +void php_swoole_server_onBufferFull(Server *serv, DataHead *info) { + 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_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_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(); - - zend_fcall_info_cache *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); @@ -2024,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); @@ -2034,121 +1854,75 @@ static int php_swoole_server_dispatch_func(Server *serv, Connection *conn, SendD return worker_id; } -void php_swoole_onBufferEmpty(Server *serv, DataHead *info) { - zval *zserv = (zval *) serv->private_data_2; +void php_swoole_server_onBufferEmpty(Server *serv, DataHead *info) { + 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 void **php_swoole_server_worker_create_buffers(Server *serv, uint buffer_num) { - zend_string **buffers = (zend_string **) sw_calloc(buffer_num, sizeof(zend_string *)); - if (buffers == nullptr) { - swError("malloc for worker input_buffers failed"); - } - return (void **) buffers; -} - -static void php_swoole_server_worker_free_buffers(Server *serv, uint buffer_num, void **buffers) { - sw_free(buffers); -} - -static sw_inline zend_string *php_swoole_server_worker_get_input_buffer(Server *serv, int reactor_id) { - zend_string **buffers = (zend_string **) serv->worker_input_buffers; - if (serv->is_base_mode()) { - return buffers[0]; - } else { - return buffers[reactor_id]; - } -} - -static sw_inline void php_swoole_server_worker_set_buffer(Server *serv, DataHead *info, zend_string *addr) { - zend_string **buffers = (zend_string **) serv->worker_input_buffers; - buffers[info->reactor_id] = addr; -} - -static void *php_swoole_server_worker_get_buffer(Server *serv, DataHead *info) { - zend_string *worker_buffer = php_swoole_server_worker_get_input_buffer(serv, info->reactor_id); +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); - if (worker_buffer == nullptr) { - worker_buffer = zend_string_alloc(info->len, 0); - worker_buffer->len = 0; - php_swoole_server_worker_set_buffer(serv, info, worker_buffer); + /* primary port */ + for (auto ls : serv->ports) { + php_swoole_server_add_port(server_object, ls); } - return worker_buffer->val + worker_buffer->len; -} - -static size_t php_swoole_server_worker_get_buffer_len(Server *serv, DataHead *info) { - zend_string *worker_buffer = php_swoole_server_worker_get_input_buffer(serv, info->reactor_id); - - return worker_buffer == nullptr ? 0 : worker_buffer->len; -} - -static void php_swoole_server_worker_add_buffer_len(Server *serv, DataHead *info, size_t len) { - zend_string *worker_buffer = php_swoole_server_worker_get_input_buffer(serv, info->reactor_id); - worker_buffer->len += len; -} + /* iterator */ + do { + zval connection_iterator; + object_init_ex(&connection_iterator, swoole_connection_iterator_ce); -static void php_swoole_server_worker_move_buffer(Server *serv, PipeBuffer *buffer) { - zend_string *worker_buffer = php_swoole_server_worker_get_input_buffer(serv, buffer->info.reactor_id); - memcpy(buffer->data, &worker_buffer, sizeof(worker_buffer)); - worker_buffer->val[worker_buffer->len] = '\0'; - php_swoole_server_worker_set_buffer(serv, &buffer->info, nullptr); -} + ConnectionIterator *iterator = php_swoole_connection_iterator_get_ptr(&connection_iterator); + iterator->serv = serv; -static size_t php_swoole_server_worker_get_packet(Server *serv, 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) { - zend_string *worker_buffer; - memcpy(&worker_buffer, req->data, sizeof(worker_buffer)); - *data_ptr = worker_buffer->val; - length = worker_buffer->len; - } else { - *data_ptr = req->data; - length = req->info.len; - } + zend_update_property(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("connections"), &connection_iterator); + zval_ptr_dtor(&connection_iterator); + } while (0); - return length; + /* info */ + auto port = serv->get_primary_port(); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("mode"), serv->get_mode()); + zend_update_property_stringl( + swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("host"), port->host.c_str(), port->host.length()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("port"), port->get_port()); + zend_update_property_long(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("type"), port->get_type()); + zend_update_property_bool(swoole_server_ce, SW_Z8_OBJ_P(zserv), ZEND_STRL("ssl"), port->ssl); } static PHP_METHOD(swoole_server, __construct) { ServerObject *server_object = server_fetch_object(Z_OBJ_P(ZEND_THIS)); Server *serv = server_object->serv; if (serv) { - php_swoole_fatal_error(E_ERROR, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + zend_throw_error(NULL, "Constructor of %s can only be called once", SW_Z_OBJCE_NAME_VAL_P(ZEND_THIS)); + RETURN_FALSE; } zval *zserv = ZEND_THIS; @@ -2156,21 +1930,14 @@ static PHP_METHOD(swoole_server, __construct) { size_t host_len = 0; zend_long sock_type = SW_SOCK_TCP; zend_long serv_port = 0; - zend_long serv_mode = Server::MODE_PROCESS; + zend_long serv_mode = Server::MODE_BASE; - // only cli env if (!SWOOLE_G(cli)) { zend_throw_exception_ex( swoole_exception_ce, -1, "%s can only be used in CLI mode", SW_Z_OBJCE_NAME_VAL_P(zserv)); RETURN_FALSE; } - if (sw_server() != nullptr) { - zend_throw_exception_ex( - swoole_exception_ce, -3, "server is running. unable to create %s", SW_Z_OBJCE_NAME_VAL_P(zserv)); - RETURN_FALSE; - } - ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) Z_PARAM_STRING(host, host_len) Z_PARAM_OPTIONAL @@ -2179,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 = (php_swoole_server_port_property *) 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) {} @@ -2246,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)); @@ -2302,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); @@ -2320,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 @@ -2378,7 +2137,7 @@ static PHP_METHOD(swoole_server, set) { #endif // task use object if (php_swoole_array_get_value(vht, "task_use_object", ztmp) || - php_swoole_array_get_value(vht, "task_object", ztmp)) { + php_swoole_array_get_value(vht, "task_object", ztmp)) { serv->task_object = zval_is_true(ztmp); } if (php_swoole_array_get_value(vht, "event_object", ztmp)) { @@ -2432,18 +2191,9 @@ static PHP_METHOD(swoole_server, set) { if (php_swoole_array_get_value(vht, "heartbeat_check_interval", ztmp)) { zend_long v = zval_get_long(ztmp); serv->heartbeat_check_interval = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - } - // heartbeat idle time - if (php_swoole_array_get_value(vht, "heartbeat_idle_time", ztmp)) { + } else if (php_swoole_array_get_value(vht, "heartbeat_idle_time", ztmp)) { zend_long v = zval_get_long(ztmp); - serv->heartbeat_idle_time = SW_MAX(0, SW_MIN(v, UINT16_MAX)); - - if (serv->heartbeat_check_interval > serv->heartbeat_idle_time) { - php_swoole_fatal_error(E_WARNING, "heartbeat_idle_time must be greater than heartbeat_check_interval"); - serv->heartbeat_check_interval = serv->heartbeat_idle_time / 2; - } - } else if (serv->heartbeat_check_interval > 0) { - serv->heartbeat_idle_time = serv->heartbeat_check_interval * 2; + serv->heartbeat_check_interval = v > 2 ? v / 2 : 1; } // max_request if (php_swoole_array_get_value(vht, "max_request", ztmp)) { @@ -2518,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) { @@ -2527,6 +2278,10 @@ static PHP_METHOD(swoole_server, set) { } serv->http_compression_level = level; } + 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 #ifdef SW_HAVE_ZLIB @@ -2544,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 */ @@ -2575,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 */ @@ -2597,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)); } /** @@ -2605,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 @@ -2613,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; } @@ -2631,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; } @@ -2639,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); @@ -2660,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 { @@ -2671,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; @@ -2707,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; } @@ -2735,64 +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); +} + +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; + } - Worker *worker = php_swoole_process_get_and_check_worker(process); - worker->ptr = process; + char *name; + size_t l_name; + zend_long accepted_process_types; + zval *zfn; - int id = serv->add_worker(worker); - if (id < 0) { - php_swoole_fatal_error(E_WARNING, "Server::add_worker() failed"); + 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; } - zend_update_property_long(swoole_process_ce, SW_Z8_OBJ_P(process), ZEND_STRL("id"), id); - RETURN_LONG(id); -} -static inline zend_bool is_websocket_server(zval *zobject) { - return instanceof_function(Z_OBJCE_P(zobject), swoole_websocket_server_ce); -} + 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}"); + } -static inline zend_bool is_http_server(zval *zobject) { - return instanceof_function(Z_OBJCE_P(zobject), swoole_http_server_ce); + return std::string(Z_STRVAL(return_value), Z_STRLEN(return_value)); + }; + + if (!serv->add_command(std::string(name, l_name), accepted_process_types, fn)) { + RETURN_FALSE; + } + + 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)); @@ -2804,46 +2659,73 @@ 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; } - php_swoole_server_register_callbacks(serv); + ServerObject *server_object = server_fetch_object(Z_OBJ_P(php_swoole_server_zval_ptr(serv))); - serv->onReceive = php_swoole_onReceive; +#ifdef SW_THREAD + zend_string *bootstrap = nullptr; + ZendArray *thread_argv = nullptr; - if (is_websocket_server(zserv) || is_http_server(zserv)) { - zval *zsetting = sw_zend_read_and_convert_property_array(swoole_server_ce, ZEND_THIS, ZEND_STRL("setting"), 0); - add_assoc_bool(zsetting, "open_http_protocol", 1); - add_assoc_bool(zsetting, "open_mqtt_protocol", 0); - add_assoc_bool(zsetting, "open_eof_check", 0); - add_assoc_bool(zsetting, "open_length_check", 0); + if (serv->is_thread_mode()) { + zval *_bootstrap = zend::object_get(ZEND_THIS, ZEND_STRL("bootstrap")); + bootstrap = zend_string_dup(Z_STR_P(_bootstrap), 1); - enum protocol_flags { SW_HTTP2_PROTOCOL = 1u << 1, SW_WEBSOCKET_PROTOCOL = 1u << 2 }; - uint8_t protocol_flag = 0; - auto primary_port = serv->get_primary_port(); - if (primary_port->open_http2_protocol) { - add_assoc_bool(zsetting, "open_http2_protocol", 1); - protocol_flag |= SW_HTTP2_PROTOCOL; + 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); } - if (primary_port->open_websocket_protocol || is_websocket_server(zserv)) { - add_assoc_bool(zsetting, "open_websocket_protocol", 1); - protocol_flag |= SW_WEBSOCKET_PROTOCOL; + + 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()); } - primary_port->clear_protocol(); - primary_port->open_http_protocol = 1; - primary_port->open_http2_protocol = !!(protocol_flag & SW_HTTP2_PROTOCOL); - primary_port->open_websocket_protocol = !!(protocol_flag & SW_WEBSOCKET_PROTOCOL); } +#endif - php_swoole_server_before_start(serv, zserv); + 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; } @@ -2875,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; } @@ -2895,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); @@ -2915,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) @@ -2926,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; } @@ -2985,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); @@ -3003,7 +2888,7 @@ static PHP_METHOD(swoole_server, close) { php_swoole_fatal_error(E_WARNING, "server is not running"); RETURN_FALSE; } - + zend_long fd; zend_bool reset = false; @@ -3024,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); @@ -3043,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); @@ -3063,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)) { @@ -3101,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) { @@ -3116,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) { @@ -3139,27 +3023,32 @@ 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_idle_time < 1) { + if (serv->heartbeat_check_interval < 1) { RETURN_FALSE; } array_init(return_value); - double checktime = microtime() - serv->heartbeat_idle_time; + double now = microtime(); - serv->foreach_connection([serv, checktime, close_connection, return_value](Connection *conn) { - swTrace("heartbeat check fd=%d", conn->fd); - if (conn->protect || conn->last_recv_time == 0 || conn->last_recv_time > checktime) { + serv->foreach_connection([serv, now, close_connection, return_value](Connection *conn) { + SessionId session_id = conn->session_id; + if (session_id <= 0) { + return; + } + swoole_trace("heartbeat check fd=%d", conn->fd); + if (serv->is_healthy_connection(now, conn)) { return; } if (close_connection) { conn->close_force = 1; - serv->close(conn->fd, false); + serv->close(session_id, false); } - add_next_index_long(return_value, conn->session_id); + add_next_index_long(return_value, session_id); }); } @@ -3175,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; - } - - TaskId task_id = buf.info.fd; + 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); - 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); + TaskCo task_co{}; + task_co.co = Coroutine::get_current_safe(); + task_co.count = 1; + task_co.result = return_value; - // 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) >= 0) { - 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"); @@ -3259,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); @@ -3281,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()) { @@ -3307,20 +3183,24 @@ static PHP_METHOD(swoole_server, taskWaitMulti) { // clear history task network::Socket *task_notify_socket = pipe->get_socket(false); task_notify_socket->set_nonblock(); - while (task_notify_socket->read(¬ify, sizeof(notify)) > 0) {} + while (task_notify_socket->read(¬ify, sizeof(notify)) > 0) { + } 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); @@ -3357,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()); @@ -3396,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; - } + TaskCo task_co; + task_co.co = Coroutine::get_current_safe(); - task_co->server_object = server_object; + array_init_size(return_value, n_task); - zval *result = sw_malloc_zval(); - array_init(result); - - 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) { @@ -3463,17 +3332,21 @@ static PHP_METHOD(swoole_server, taskCo) { RETURN_FALSE; } - long ms = (long) (timeout * 1000); - - task_co->result = result; - task_co->list = list; - task_co->count = n_task; + task_co.result = return_value; + 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) { @@ -3486,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; - if (serv->gs->task_workers.dispatch(&buf, &_dst_worker_id) >= 0) { - RETURN_LONG(buf.info.fd); + 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; } - sw_atomic_fetch_sub(&serv->gs->tasking_num, 1); - 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); + + smart_str buf = {}; + if (php_json_encode(&buf, zdata, 0) == FAILURE || !buf.s) { + 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) { @@ -3542,31 +3471,26 @@ 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; } - if (worker_id >= serv->worker_num + serv->task_worker_num) { + if (worker_id < 0 || worker_id >= serv->worker_num + serv->task_worker_num) { php_swoole_fatal_error(E_WARNING, "worker_id[%d] is invalid", (int) worker_id); RETURN_FALSE; } 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) { @@ -3588,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) { @@ -3605,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) { @@ -3636,17 +3570,18 @@ 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); RETURN_FALSE; } - Connection *conn = serv->get_connection_by_session_id(fd); - if (conn == nullptr || conn->active == 0) { + Connection *conn = serv->get_connection_verify(fd); + if (conn == nullptr) { RETURN_FALSE; } @@ -3666,12 +3601,13 @@ 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); - php_socket *socket_object = php_swoole_convert_to_socket(lp->socket->fd); + const ListenPort *lp = serv->get_port(port); + php_socket *socket_object = php_swoole_convert_to_socket(lp->get_fd()); if (!socket_object) { RETURN_FALSE; @@ -3693,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) { @@ -3707,9 +3646,12 @@ 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 == Server::DISPATCH_CO_CONN_LB) { + add_assoc_long(return_value, "worker_id", conn->worker_id); + } ListenPort *port = serv->get_port_by_fd(conn->fd); if (port && port->open_websocket_protocol) { @@ -3723,18 +3665,18 @@ 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); add_assoc_long(return_value, "socket_type", conn->socket_type); add_assoc_long(return_value, "remote_port", conn->info.get_port()); - add_assoc_string(return_value, "remote_ip", (char * ) conn->info.get_ip()); + add_assoc_string(return_value, "remote_ip", (char *) conn->info.get_ip()); add_assoc_long(return_value, "reactor_id", conn->reactor_id); add_assoc_long(return_value, "connect_time", conn->connect_time); - add_assoc_long(return_value, "last_time", (int ) conn->last_recv_time); + add_assoc_long(return_value, "last_time", (int) conn->last_recv_time); add_assoc_double(return_value, "last_recv_time", conn->last_recv_time); add_assoc_double(return_value, "last_send_time", conn->last_send_time); add_assoc_double(return_value, "last_dispatch_time", conn->last_dispatch_time); @@ -3751,12 +3693,14 @@ static PHP_METHOD(swoole_server, getClientList) { RETURN_FALSE; } - zend_long start_fd = 0; + zend_long start_session_id = 0; zend_long find_count = 10; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &start_fd, &find_count) == FAILURE) { - RETURN_FALSE; - } + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(start_session_id) + Z_PARAM_LONG(find_count) + ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); // exceeded the maximum number of searches if (find_count > SW_MAX_FIND_COUNT) { @@ -3766,11 +3710,12 @@ static PHP_METHOD(swoole_server, getClientList) { // copy it out to avoid being overwritten by other processes int serv_max_fd = serv->get_maxfd(); + int start_fd; - if (start_fd == 0) { + if (start_session_id == 0) { start_fd = serv->get_minfd(); } else { - Connection *conn = serv->get_connection_by_session_id(start_fd); + Connection *conn = serv->get_connection_verify(start_session_id); if (!conn) { RETURN_FALSE; } @@ -3783,19 +3728,20 @@ static PHP_METHOD(swoole_server, getClientList) { array_init(return_value); int fd = start_fd + 1; - Connection *conn; for (; fd <= serv_max_fd; fd++) { - swTrace("maxfd=%d, fd=%d, find_count=%ld, start_fd=%ld", serv_max_fd, fd, find_count, start_fd); - conn = serv->get_connection(fd); - - if (conn->active && !conn->closed) { -#ifdef SW_USE_OPENSSL - if (conn->ssl && !conn->ssl_ready) { + 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; + if (session_id <= 0) { continue; } -#endif - add_next_index_long(return_value, conn->session_id); + add_next_index_long(return_value, session_id); find_count--; } // finish fetch @@ -3815,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; } @@ -3842,18 +3789,14 @@ static PHP_METHOD(swoole_server, exists) { RETURN_FALSE; } - zend_long fd; + zend_long session_id; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(fd) + Z_PARAM_LONG(session_id) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); - Connection *conn = serv->get_connection_by_session_id(fd); - if (!conn) { - RETURN_FALSE; - } - // connection is closed - if (conn->active == 0 || conn->closed) { + Connection *conn = serv->get_connection_verify(session_id); + if (!conn || conn->closed) { RETURN_FALSE; } else { RETURN_TRUE; @@ -3867,19 +3810,17 @@ static PHP_METHOD(swoole_server, protect) { RETURN_FALSE; } - zend_long fd; + zend_long session_id; zend_bool value = 1; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l|b", &fd, &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_by_session_id(fd); - if (!conn) { - RETURN_FALSE; - } - // connection is closed - if (conn->active == 0 || conn->closed) { + Connection *conn = serv->get_connection_verify(session_id); + if (!conn || conn->closed) { RETURN_FALSE; } else { conn->protect = value; @@ -3889,10 +3830,10 @@ static PHP_METHOD(swoole_server, protect) { static PHP_METHOD(swoole_server, getWorkerId) { Server *serv = php_swoole_server_get_and_check_server(ZEND_THIS); - if (!serv->is_worker()) { + if (!serv->is_worker() && !serv->is_task_worker()) { RETURN_FALSE; } else { - RETURN_LONG(SwooleG.process_id); + RETURN_LONG(swoole_get_process_id()); } } @@ -3904,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 { @@ -3924,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()) { - 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) { @@ -3971,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; } @@ -4014,28 +3929,22 @@ static PHP_METHOD(swoole_connection_iterator, rewind) { static PHP_METHOD(swoole_connection_iterator, valid) { ConnectionIterator *iterator = php_swoole_connection_iterator_get_and_check_ptr(ZEND_THIS); int fd = iterator->current_fd; - Connection *conn; - int max_fd = iterator->serv->get_maxfd(); for (; fd <= max_fd; fd++) { - conn = iterator->serv->get_connection(fd); - - if (conn->active && !conn->closed) { -#ifdef SW_USE_OPENSSL - if (conn->ssl && !conn->ssl_ready) { - continue; - } -#endif - if (iterator->port && - (iterator->port->socket_fd < 0 || conn->server_fd != iterator->port->socket_fd)) { - continue; - } - iterator->session_id = conn->session_id; - iterator->current_fd = fd; - iterator->index++; - RETURN_TRUE; + Connection *conn = iterator->serv->get_connection_for_iterator(fd); + if (!conn) { + continue; + } + SessionId session_id = conn->session_id; + if (session_id <= 0 || + (iterator->port && (iterator->port->get_fd() < 0 || conn->server_fd != iterator->port->get_fd()))) { + continue; } + iterator->session_id = session_id; + iterator->current_fd = fd; + iterator->index++; + RETURN_TRUE; } RETURN_FALSE; @@ -4059,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 f20d9151b33..9e5fdadb0a8 100644 --- a/ext-src/swoole_server_port.cc +++ b/ext-src/swoole_server_port.cc @@ -10,46 +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") }, +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; - php_swoole_server_port_property property; + 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) { @@ -59,7 +66,7 @@ static sw_inline ListenPort *php_swoole_server_port_get_ptr(zval *zobject) { ListenPort *php_swoole_server_port_get_and_check_ptr(zval *zobject) { ListenPort *port = php_swoole_server_port_get_ptr(zobject); if (UNEXPECTED(!port)) { - php_swoole_fatal_error(E_ERROR, "Invaild instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + php_swoole_fatal_error(E_ERROR, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); } return port; } @@ -68,27 +75,27 @@ void php_swoole_server_port_set_ptr(zval *zobject, ListenPort *port) { php_swoole_server_port_fetch_object(Z_OBJ_P(zobject))->port = port; } -php_swoole_server_port_property *php_swoole_server_port_get_property(zval *zobject) { +ServerPortProperty *php_swoole_server_port_get_property(zval *zobject) { return &php_swoole_server_port_fetch_object(Z_OBJ_P(zobject))->property; } -static php_swoole_server_port_property *php_swoole_server_port_get_and_check_property(zval *zobject) { - php_swoole_server_port_property *property = php_swoole_server_port_get_property(zobject); +static ServerPortProperty *php_swoole_server_port_get_and_check_property(zval *zobject) { + ServerPortProperty *property = php_swoole_server_port_get_property(zobject); if (UNEXPECTED(!property->serv)) { - php_swoole_fatal_error(E_ERROR, "Invaild instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); + php_swoole_fatal_error(E_ERROR, "Invalid instance of %s", SW_Z_OBJCE_NAME_VAL_P(zobject)); } return property; } // 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); - php_swoole_server_port_property *property = &server_port->property; + 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; @@ -96,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; } @@ -111,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; @@ -131,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); @@ -181,34 +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); @@ -216,8 +206,6 @@ static ssize_t php_swoole_server_length_func(Protocol *protocol, network::Socket } 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); @@ -227,8 +215,8 @@ static ssize_t php_swoole_server_length_func(Protocol *protocol, network::Socket } 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) {} @@ -268,7 +256,7 @@ static PHP_METHOD(swoole_server_port, set) { vht = Z_ARRVAL_P(zset); ListenPort *port = php_swoole_server_port_get_and_check_ptr(ZEND_THIS); - php_swoole_server_port_property *property = php_swoole_server_port_get_and_check_property(ZEND_THIS); + ServerPortProperty *property = php_swoole_server_port_get_and_check_property(ZEND_THIS); if (port == nullptr || property == nullptr) { php_swoole_fatal_error(E_ERROR, "please use the swoole_server->listen method"); @@ -281,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; @@ -291,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; @@ -301,18 +289,23 @@ 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; } } - if (php_swoole_array_get_value(vht, "buffer_high_watermark", ztmp)) { + // heartbeat idle time + if (php_swoole_array_get_value(vht, "heartbeat_idle_time", ztmp)) { zend_long v = zval_get_long(ztmp); + 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 = 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 @@ -377,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); @@ -385,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); @@ -395,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)) { @@ -431,7 +423,7 @@ static PHP_METHOD(swoole_server_port, set) { port->protocol.package_length_type = str_v.val()[0]; port->protocol.package_length_size = swoole_type_size(port->protocol.package_length_type); if (port->protocol.package_length_size == 0) { - php_swoole_fatal_error(E_ERROR, "unknow package_length_type, see pack(). Link: http://php.net/pack"); + php_swoole_fatal_error(E_ERROR, "unknown package_length_type, see pack(). Link: http://php.net/pack"); RETURN_FALSE; } } @@ -454,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)); } @@ -508,9 +478,11 @@ static PHP_METHOD(swoole_server_port, set) { if (php_swoole_array_get_value(vht, "ssl_protocols", ztmp)) { zend_long v = zval_get_long(ztmp); port->ssl_context->protocols = v; +#ifdef SW_SUPPORT_DTLS if (port->is_dtls() && !port->is_dgram()) { port->ssl_context->protocols ^= SW_SSL_DTLS; } +#endif } if (php_swoole_array_get_value(vht, "ssl_verify_peer", ztmp)) { port->ssl_context->verify_peer = zval_is_true(ztmp); @@ -578,13 +550,16 @@ static PHP_METHOD(swoole_server_port, set) { SSLContext *context = new SSLContext(); *context = *port->ssl_context; if (!php_swoole_server_set_ssl_option(Z_ARRVAL_P(current), context)) { + delete context; RETURN_FALSE; } if (!port->ssl_add_sni_cert(std::string(key->val, key->len), context)) { php_swoole_fatal_error(E_ERROR, "ssl_add_sni_cert() failed"); + delete context; RETURN_FALSE; } - } ZEND_HASH_FOREACH_END(); + } + ZEND_HASH_FOREACH_END(); } if (!port->ssl_context->cert_file.empty() || port->sni_contexts.empty()) { @@ -614,81 +589,60 @@ static PHP_METHOD(swoole_server_port, set) { static PHP_METHOD(swoole_server_port, on) { char *name = nullptr; - size_t len, i; + size_t len; zval *cb; - php_swoole_server_port_property *property = php_swoole_server_port_get_and_check_property(ZEND_THIS); + 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); - - const char *callback_name[PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM] = { - "Connect", - "Receive", - "Close", - "Packet", - "Request", - "HandShake", - "Open", - "Message", - "BufferFull", - "BufferEmpty", - }; - - char property_name[128]; - int l_property_name = 0; - memcpy(property_name, "on", 2); - - for (i = 0; i < PHP_SWOOLE_SERVER_PORT_CALLBACK_NUM; i++) { - if (!swoole_strcaseeq(name, len, callback_name[i], strlen(callback_name[i]))) { + + bool found = false; + for (auto i = server_port_event_map.begin(); i != server_port_event_map.end(); i++) { + if (!swoole_strcaseeq(name, len, i->first.c_str(), i->first.length())) { continue; } - memcpy(property_name + 2, callback_name[i], len); - l_property_name = len + 2; - property_name[l_property_name] = '\0'; - zend_update_property(swoole_server_port_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name, l_property_name, cb); - property->callbacks[i] = - sw_zend_read_property(swoole_server_port_ce, ZEND_THIS, property_name, l_property_name, 0); - sw_copy_to_stack(property->callbacks[i], property->_callbacks[i]); - if (property->caches[i]) { - efree(property->caches[i]); - } - property->caches[i] = fci_cache; - - if (i == SW_SERVER_CB_onConnect && !serv->onConnect) { - serv->onConnect = php_swoole_onConnect; - } else if (i == SW_SERVER_CB_onPacket && !serv->onPacket) { - serv->onPacket = php_swoole_onPacket; - } else if (i == SW_SERVER_CB_onClose && !serv->onClose) { - serv->onClose = php_swoole_onClose; - } else if (i == SW_SERVER_CB_onBufferFull && !serv->onBufferFull) { - serv->onBufferFull = php_swoole_onBufferFull; - } else if (i == SW_SERVER_CB_onBufferEmpty && !serv->onBufferEmpty) { - serv->onBufferEmpty = php_swoole_onBufferEmpty; - } else if (i == SW_SERVER_CB_onMessage || i == SW_SERVER_CB_onRequest) { - serv->onReceive = php_swoole_http_onReceive; + found = true; + int index = i->second.type; + 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); + + if (property->callbacks[index]) { + sw_callable_free(property->callbacks[index]); + } + + auto fci_cache = sw_callable_create(cb); + if (!fci_cache) { + RETURN_FALSE; + } + property->callbacks[index] = fci_cache; + + if (index == SW_SERVER_CB_onConnect && !serv->onConnect) { + serv->onConnect = php_swoole_server_onConnect; + } else if (index == SW_SERVER_CB_onPacket && !serv->onPacket) { + serv->onPacket = php_swoole_server_onPacket; + } else if (index == SW_SERVER_CB_onClose && !serv->onClose) { + serv->onClose = php_swoole_server_onClose; + } else if (index == SW_SERVER_CB_onBufferFull && !serv->onBufferFull) { + serv->onBufferFull = php_swoole_server_onBufferFull; + } else if (index == SW_SERVER_CB_onBufferEmpty && !serv->onBufferEmpty) { + serv->onBufferEmpty = php_swoole_server_onBufferEmpty; } break; } - if (l_property_name == 0) { + if (!found) { php_swoole_error(E_WARNING, "unknown event types[%s]", name); - efree(fci_cache); RETURN_FALSE; } RETURN_TRUE; @@ -719,7 +673,7 @@ static PHP_METHOD(swoole_server_port, getCallback) { #ifdef SWOOLE_SOCKETS_SUPPORT static PHP_METHOD(swoole_server_port, getSocket) { ListenPort *port = php_swoole_server_port_get_and_check_ptr(ZEND_THIS); - php_socket *socket_object = php_swoole_convert_to_socket(port->socket->fd); + php_socket *socket_object = php_swoole_convert_to_socket(port->get_fd()); if (!socket_object) { RETURN_FALSE; } diff --git a/ext-src/swoole_socket_coro.cc b/ext-src/swoole_socket_coro.cc index 824f4f1bc67..0c762eded08 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -12,19 +12,31 @@ | to obtain it through the world-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" #include "swoole_protocol.h" #include "swoole_mqtt.h" + #include "thirdparty/php/sockets/php_sockets_cxx.h" #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; @@ -36,6 +48,7 @@ static zend_object_handlers swoole_socket_coro_exception_handlers; struct SocketObject { Socket *socket; + zval zstream; bool reference; zend_object std; }; @@ -47,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); @@ -58,6 +72,8 @@ static PHP_METHOD(swoole_socket_coro, sendFile); static PHP_METHOD(swoole_socket_coro, recvAll); static PHP_METHOD(swoole_socket_coro, sendAll); static PHP_METHOD(swoole_socket_coro, recvPacket); +static PHP_METHOD(swoole_socket_coro, recvLine); +static PHP_METHOD(swoole_socket_coro, recvWithBuffer); static PHP_METHOD(swoole_socket_coro, recvfrom); static PHP_METHOD(swoole_socket_coro, sendto); static PHP_METHOD(swoole_socket_coro, getOption); @@ -71,182 +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, 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, recvAll, arginfo_swoole_socket_coro_recv, 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(), @@ -257,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); @@ -794,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) { @@ -866,12 +791,45 @@ 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 = socket_coro_create_object(swoole_socket_coro_ce); + SocketObject *sock = (SocketObject *) socket_coro_fetch_object(object); + + sock->socket = new Socket(type); + 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 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 = 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 = socket; if (UNEXPECTED(sock->socket->get_fd() < 0)) { php_swoole_sys_error(E_WARNING, "new Socket() failed"); delete sock->socket; @@ -881,22 +839,35 @@ 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 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) { @@ -955,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; @@ -973,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 @@ -1005,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; @@ -1047,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]; @@ -1058,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) { @@ -1096,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) { @@ -1126,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; @@ -1143,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; @@ -1161,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; } } @@ -1193,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; @@ -1204,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; @@ -1214,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; @@ -1234,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; @@ -1246,7 +1414,14 @@ static PHP_METHOD(swoole_socket_coro, peek) { } } -static inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, const bool all) { +enum RecvMode { + SOCKET_RECV, + SOCKET_RECV_ALL, + SOCKET_RECV_LINE, + SOCKET_RECV_WITH_BUFFER, +}; + +static inline void socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, RecvMode type) { zend_long length = SW_BUFFER_SIZE_BIG; double timeout = 0; @@ -1264,8 +1439,25 @@ static inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, const b zend_string *buf = zend_string_alloc(length, 0); Socket::TimeoutSetter ts(sock->socket, timeout, Socket::TIMEOUT_READ); - ssize_t bytes = all ? sock->socket->recv_all(ZSTR_VAL(buf), length) : sock->socket->recv(ZSTR_VAL(buf), length); - swoole_socket_coro_sync_properties(ZEND_THIS, sock); + ssize_t bytes = -1; + switch (type) { + case SOCKET_RECV: + bytes = sock->socket->recv(ZSTR_VAL(buf), length); + break; + case SOCKET_RECV_ALL: + bytes = sock->socket->recv_all(ZSTR_VAL(buf), length); + break; + case SOCKET_RECV_LINE: + bytes = sock->socket->recv_line(ZSTR_VAL(buf), length); + break; + case SOCKET_RECV_WITH_BUFFER: + bytes = sock->socket->recv_with_buffer(ZSTR_VAL(buf), length); + break; + default: + assert(0); + break; + } + socket_coro_sync_properties(ZEND_THIS, sock); if (UNEXPECTED(bytes < 0)) { zend_string_free(buf); RETURN_FALSE; @@ -1278,11 +1470,19 @@ static inline void swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAMETERS, const b } static PHP_METHOD(swoole_socket_coro, recv) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV); } static PHP_METHOD(swoole_socket_coro, recvAll) { - swoole_socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_ALL); +} + +static PHP_METHOD(swoole_socket_coro, recvLine) { + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_LINE); +} + +static PHP_METHOD(swoole_socket_coro, recvWithBuffer) { + socket_coro_recv(INTERNAL_FUNCTION_PARAM_PASSTHRU, SOCKET_RECV_WITH_BUFFER); } static PHP_METHOD(swoole_socket_coro, recvPacket) { @@ -1295,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) { @@ -1307,12 +1507,12 @@ static PHP_METHOD(swoole_socket_coro, recvPacket) { sock->socket->set_err(ENOMEM); RETURN_FALSE; } else { - sw_set_zend_string(return_value, strval, retval); + zend::assign_zend_string_by_val(return_value, strval, retval); } } } -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; @@ -1327,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 { @@ -1336,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; @@ -1356,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; } @@ -1401,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; @@ -1426,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); @@ -1494,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; @@ -1507,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) { @@ -1520,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; @@ -1541,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) { @@ -1559,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; @@ -1594,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 { @@ -1613,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; @@ -1622,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; } @@ -1641,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; } @@ -1655,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; } @@ -1670,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(); @@ -1728,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); @@ -1769,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); @@ -1917,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 84468fafb2a..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,29 +18,29 @@ #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) { array_init(return_value); - TableStringLength vlen = 0; - double dval = 0; - long lval = 0; - for (auto i = table->column_list->begin(); i != table->column_list->end(); i++) { TableColumn *col = *i; if (col->type == TableColumn::TYPE_STRING) { - memcpy(&vlen, row->data + col->index, sizeof(TableStringLength)); - add_assoc_stringl_ex(return_value, - col->name.c_str(), - col->name.length(), - row->data + col->index + sizeof(TableStringLength), - vlen); + TableStringLength len = 0; + char *str = nullptr; + row->get_value(col, &str, &len); + add_assoc_stringl_ex(return_value, col->name.c_str(), col->name.length(), str, len); } else if (col->type == TableColumn::TYPE_FLOAT) { - memcpy(&dval, row->data + col->index, sizeof(dval)); + double dval = 0; + row->get_value(col, &dval); add_assoc_double_ex(return_value, col->name.c_str(), col->name.length(), dval); } else if (col->type == TableColumn::TYPE_INT) { - memcpy(&lval, row->data + col->index, sizeof(lval)); + long lval = 0; + row->get_value(col, &lval); add_assoc_long_ex(return_value, col->name.c_str(), col->name.length(), lval); } else { abort(); @@ -50,23 +50,23 @@ static inline void php_swoole_table_row2array(Table *table, TableRow *row, zval static inline void php_swoole_table_get_field_value( Table *table, TableRow *row, zval *return_value, char *field, uint16_t field_len) { - TableStringLength vlen = 0; - double dval = 0; - long lval = 0; - TableColumn *col = table->get_column(std::string(field, field_len)); if (!col) { ZVAL_FALSE(return_value); return; } if (col->type == TableColumn::TYPE_STRING) { - memcpy(&vlen, row->data + col->index, sizeof(TableStringLength)); - ZVAL_STRINGL(return_value, row->data + col->index + sizeof(TableStringLength), vlen); + TableStringLength len = 0; + char *str = nullptr; + row->get_value(col, &str, &len); + ZVAL_STRINGL(return_value, str, len); } else if (col->type == TableColumn::TYPE_FLOAT) { - memcpy(&dval, row->data + col->index, sizeof(dval)); + double dval = 0; + row->get_value(col, &dval); ZVAL_DOUBLE(return_value, dval); } else if (col->type == TableColumn::TYPE_INT) { - memcpy(&lval, row->data + col->index, sizeof(lval)); + long lval = 0; + row->get_value(col, &lval); ZVAL_LONG(return_value, lval); } else { abort(); @@ -76,9 +76,6 @@ static inline void php_swoole_table_get_field_value( static zend_class_entry *swoole_table_ce; static zend_object_handlers swoole_table_handlers; -static zend_class_entry *swoole_table_row_ce; -static zend_object_handlers swoole_table_row_handlers; - struct TableObject { Table *ptr; zend_object std; @@ -94,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; } @@ -124,106 +121,6 @@ static inline zend_object *php_swoole_table_create_object(zend_class_entry *ce) return &table->std; } -struct TableRowObject { - Table *ptr; - zend_object std; -}; - -static inline TableRowObject *php_swoole_table_row_fetch_object(zend_object *obj) { - return (TableRowObject *) ((char *) obj - swoole_table_row_handlers.offset); -} - -static inline Table *php_swoole_table_row_get_ptr(zval *zobject) { - return php_swoole_table_row_fetch_object(Z_OBJ_P(zobject))->ptr; -} - -static inline Table *php_swoole_table_row_get_and_check_ptr(zval *zobject) { - Table *table_row = php_swoole_table_row_get_ptr(zobject); - if (!table_row) { - php_swoole_fatal_error(E_ERROR, "you can only get Table\\Row from Table"); - } - return table_row; -} - -static inline void php_swoole_table_row_set_ptr(zval *zobject, Table *ptr) { - php_swoole_table_row_fetch_object(Z_OBJ_P(zobject))->ptr = ptr; -} - -static inline void php_swoole_table_row_free_object(zend_object *object) { - zend_object_std_dtor(object); -} - -static inline zend_object *php_swoole_table_row_create_object(zend_class_entry *ce) { - TableRowObject *table_row = (TableRowObject *) zend_object_alloc(sizeof(TableRowObject), ce); - zend_object_std_init(&table_row->std, ce); - object_properties_init(&table_row->std, ce); - table_row->std.handlers = &swoole_table_row_handlers; - return &table_row->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_offsetExists, 0, 0, 1) - ZEND_ARG_INFO(0, offset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_offsetGet, 0, 0, 1) - ZEND_ARG_INFO(0, offset) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_offsetSet, 0, 0, 2) - ZEND_ARG_INFO(0, offset) - ZEND_ARG_INFO(0, value) -ZEND_END_ARG_INFO() - -ZEND_BEGIN_ARG_INFO_EX(arginfo_swoole_table_offsetUnset, 0, 0, 1) - ZEND_ARG_INFO(0, offset) -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); @@ -238,10 +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, offsetExists); -static PHP_METHOD(swoole_table, offsetGet); -static PHP_METHOD(swoole_table, offsetSet); -static PHP_METHOD(swoole_table, offsetUnset); +static PHP_METHOD(swoole_table, stats); static PHP_METHOD(swoole_table, rewind); static PHP_METHOD(swoole_table, next); @@ -249,64 +143,45 @@ static PHP_METHOD(swoole_table, current); static PHP_METHOD(swoole_table, key); static PHP_METHOD(swoole_table, valid); -static PHP_METHOD(swoole_table_row, offsetExists); -static PHP_METHOD(swoole_table_row, offsetGet); -static PHP_METHOD(swoole_table_row, offsetSet); -static PHP_METHOD(swoole_table_row, offsetUnset); -static PHP_METHOD(swoole_table_row, __destruct); 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) - // implement ArrayAccess - PHP_ME(swoole_table, offsetExists, arginfo_swoole_table_offsetExists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, offsetGet, arginfo_swoole_table_offsetGet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, offsetSet, arginfo_swoole_table_offsetSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table, offsetUnset, arginfo_swoole_table_offsetUnset, 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, 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, valid, arginfo_swoole_table_void, ZEND_ACC_PUBLIC) - PHP_FE_END -}; - -static const zend_function_entry swoole_table_row_methods[] = -{ - PHP_ME(swoole_table_row, offsetExists, arginfo_swoole_table_offsetExists, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table_row, offsetGet, arginfo_swoole_table_offsetGet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table_row, offsetSet, arginfo_swoole_table_offsetSet, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table_row, offsetUnset, arginfo_swoole_table_offsetUnset, ZEND_ACC_PUBLIC) - PHP_ME(swoole_table_row, __destruct, 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( swoole_table, php_swoole_table_create_object, php_swoole_table_free_object, TableObject, std); - zend_class_implements(swoole_table_ce, 2, zend_ce_iterator, zend_ce_arrayaccess); + zend_class_implements(swoole_table_ce, 1, zend_ce_iterator); #ifdef SW_HAVE_COUNTABLE zend_class_implements(swoole_table_ce, 1, zend_ce_countable); #endif @@ -317,23 +192,13 @@ void php_swoole_table_minit(int module_number) { zend_declare_class_constant_long(swoole_table_ce, ZEND_STRL("TYPE_INT"), TableColumn::TYPE_INT); zend_declare_class_constant_long(swoole_table_ce, ZEND_STRL("TYPE_STRING"), TableColumn::TYPE_STRING); zend_declare_class_constant_long(swoole_table_ce, ZEND_STRL("TYPE_FLOAT"), TableColumn::TYPE_FLOAT); - - SW_INIT_CLASS_ENTRY(swoole_table_row, "Swoole\\Table\\Row", "swoole_table_row", nullptr, swoole_table_row_methods); - SW_SET_CLASS_SERIALIZABLE(swoole_table_row, zend_class_serialize_deny, zend_class_unserialize_deny); - SW_SET_CLASS_CLONEABLE(swoole_table_row, sw_zend_class_clone_deny); - SW_SET_CLASS_UNSET_PROPERTY_HANDLER(swoole_table_row, sw_zend_class_unset_property_deny); - SW_SET_CLASS_CUSTOM_OBJECT( - swoole_table_row, php_swoole_table_row_create_object, php_swoole_table_row_free_object, TableRowObject, std); - zend_class_implements(swoole_table_row_ce, 1, zend_ce_arrayaccess); - - zend_declare_property_null(swoole_table_row_ce, ZEND_STRL("key"), ZEND_ACC_PUBLIC); - zend_declare_property_null(swoole_table_row_ce, ZEND_STRL("value"), ZEND_ACC_PUBLIC); } 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; @@ -351,7 +216,7 @@ PHP_METHOD(swoole_table, __construct) { RETURN_FALSE; } table->set_hash_func([](const char *key, size_t len) -> uint64_t { - return zend_string_hash_val(sw_get_zend_string((void *) key)); + return zend_string_hash_val(zend::fetch_zend_string_by_val((void *) key)); }); php_swoole_table_set_ptr(ZEND_THIS, table); } @@ -388,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; } @@ -436,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); @@ -482,10 +348,6 @@ static PHP_METHOD(swoole_table, set) { RETURN_TRUE; } -static PHP_METHOD(swoole_table, offsetSet) { - ZEND_MN(swoole_table_set)(INTERNAL_FUNCTION_PARAM_PASSTHRU); -} - static PHP_METHOD(swoole_table, incr) { Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); char *key; @@ -515,7 +377,7 @@ static PHP_METHOD(swoole_table, incr) { } if (out_flags & SW_TABLE_FLAG_NEW_ROW) { - column->clear(row); + table->clear_row(row); } if (column->type == TableColumn::TYPE_STRING) { @@ -575,7 +437,7 @@ static PHP_METHOD(swoole_table, decr) { } if (out_flags & SW_TABLE_FLAG_NEW_ROW) { - column->clear(row); + table->clear_row(row); } if (column->type == TableColumn::TYPE_STRING) { @@ -631,34 +493,6 @@ static PHP_METHOD(swoole_table, get) { _rowlock->unlock(); } -static PHP_METHOD(swoole_table, offsetGet) { - Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - char *key; - size_t keylen; - char *field = nullptr; - size_t field_len = 0; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &key, &keylen, &field, &field_len) == FAILURE) { - RETURN_FALSE; - } - - zval value; - TableRow *_rowlock = nullptr; - TableRow *row = table->get(key, keylen, &_rowlock); - if (!row) { - array_init(&value); - } else { - php_swoole_table_row2array(table, row, &value); - } - _rowlock->unlock(); - - object_init_ex(return_value, swoole_table_row_ce); - zend_update_property(swoole_table_row_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("value"), &value); - zend_update_property_stringl(swoole_table_row_ce, SW_Z8_OBJ_P(return_value), ZEND_STRL("key"), key, keylen); - zval_ptr_dtor(&value); - php_swoole_table_row_set_ptr(return_value, table); -} - static PHP_METHOD(swoole_table, exists) { Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); char *key; @@ -667,19 +501,7 @@ static PHP_METHOD(swoole_table, exists) { if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &keylen) == FAILURE) { RETURN_FALSE; } - - TableRow *_rowlock = nullptr; - TableRow *row = table->get(key, keylen, &_rowlock); - _rowlock->unlock(); - if (!row) { - RETURN_FALSE; - } else { - RETURN_TRUE; - } -} - -static PHP_METHOD(swoole_table, offsetExists) { - ZEND_MN(swoole_table_exists)(INTERNAL_FUNCTION_PARAM_PASSTHRU); + RETURN_BOOL(table->exists(key, keylen)); } static PHP_METHOD(swoole_table, del) { @@ -694,21 +516,15 @@ static PHP_METHOD(swoole_table, del) { RETURN_BOOL(table->del(key, keylen)); } -static PHP_METHOD(swoole_table, offsetUnset) { - ZEND_MN(swoole_table_del)(INTERNAL_FUNCTION_PARAM_PASSTHRU); -} - static PHP_METHOD(swoole_table, count) { #define COUNT_NORMAL 0 #define COUNT_RECURSIVE 1 - Table *table = php_swoole_table_get_ptr(ZEND_THIS); if (!table) { RETURN_LONG(0); } zend_long mode = COUNT_NORMAL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &mode) == FAILURE) { RETURN_FALSE; } @@ -722,7 +538,6 @@ static PHP_METHOD(swoole_table, count) { static PHP_METHOD(swoole_table, getMemorySize) { Table *table = php_swoole_table_get_ptr(ZEND_THIS); - if (!table) { RETURN_LONG(0); } else { @@ -732,7 +547,6 @@ static PHP_METHOD(swoole_table, getMemorySize) { static PHP_METHOD(swoole_table, getSize) { Table *table = php_swoole_table_get_ptr(ZEND_THIS); - if (!table) { RETURN_LONG(0); } else { @@ -740,132 +554,53 @@ 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(); table->forward(); } +static PHP_METHOD(swoole_table, valid) { + Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); + auto key = table->current(); + RETURN_BOOL(key->key_len != 0); +} + static PHP_METHOD(swoole_table, current) { Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - TableRow *row = table->current(); - if (row) { - row->lock(); - php_swoole_table_row2array(table, row, return_value); - row->unlock(); - } else { + auto row = table->current(); + if (row->key_len == 0) { RETURN_NULL(); } + php_swoole_table_row2array(table, row, return_value); } static PHP_METHOD(swoole_table, key) { Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - TableRow *row = table->current(); - if (row) { - row->lock(); - RETVAL_STRINGL(row->key, row->key_len); - row->unlock(); - } else { + auto row = table->current(); + if (row->key_len == 0) { RETURN_NULL(); } + RETVAL_STRINGL(row->key, row->key_len); } static PHP_METHOD(swoole_table, next) { Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); table->forward(); } - -static PHP_METHOD(swoole_table, valid) { - Table *table = php_swoole_table_get_and_check_ptr2(ZEND_THIS); - TableRow *row = table->current(); - RETURN_BOOL(row != nullptr); -} - -static PHP_METHOD(swoole_table_row, offsetExists) { - char *key; - size_t keylen; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &keylen) == FAILURE) { - RETURN_FALSE; - } - - zval *zprop_value = sw_zend_read_property_ex(swoole_table_row_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_VALUE), 0); - RETURN_BOOL(zend_hash_str_exists(Z_ARRVAL_P(zprop_value), key, keylen)); -} - -static PHP_METHOD(swoole_table_row, offsetGet) { - char *key; - size_t keylen; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &key, &keylen) == FAILURE) { - RETURN_FALSE; - } - - zval *zprop_value = sw_zend_read_property_ex(swoole_table_row_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_VALUE), 0); - zval *retval = nullptr; - if (!(retval = zend_hash_str_find(Z_ARRVAL_P(zprop_value), key, keylen))) { - RETURN_FALSE; - } - RETURN_ZVAL(retval, 1, 0); -} - -static PHP_METHOD(swoole_table_row, offsetSet) { - Table *table = php_swoole_table_row_get_and_check_ptr(ZEND_THIS); - zval *value; - char *key; - size_t keylen; - - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz", &key, &keylen, &value) == FAILURE) { - RETURN_FALSE; - } - - zval *zprop_key = sw_zend_read_property_ex(swoole_table_row_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_KEY), 0); - - int out_flags; - TableRow *_rowlock = nullptr; - TableRow *row = table->set(Z_STRVAL_P(zprop_key), Z_STRLEN_P(zprop_key), &_rowlock, &out_flags); - if (!row) { - _rowlock->unlock(); - php_swoole_error(E_WARNING, "Unable to allocate memory"); - RETURN_FALSE; - } - - if (out_flags & SW_TABLE_FLAG_NEW_ROW) { - for (auto i = table->column_list->begin(); i != table->column_list->end(); i++) { - (*i)->clear(row); - } - } - - TableColumn *column = table->get_column(std::string(key, keylen)); - if (column == nullptr) { - _rowlock->unlock(); - php_swoole_fatal_error(E_WARNING, "column[%s] does not exist", key); - RETURN_FALSE; - } - - if (column->type == TableColumn::TYPE_STRING) { - zend_string *str = zval_get_string(value); - row->set_value(column, ZSTR_VAL(str), ZSTR_LEN(str)); - zend_string_release(str); - } else if (column->type == TableColumn::TYPE_FLOAT) { - double _value = zval_get_double(value); - row->set_value(column, &_value, 0); - } else { - long _value = zval_get_long(value); - row->set_value(column, &_value, 0); - } - _rowlock->unlock(); - - zval *zprop_value = sw_zend_read_property_ex(swoole_table_row_ce, ZEND_THIS, SW_ZSTR_KNOWN(SW_ZEND_STR_VALUE), 0); - Z_TRY_ADDREF_P(value); - add_assoc_zval_ex(zprop_value, key, keylen, value); - - RETURN_TRUE; -} - -static PHP_METHOD(swoole_table_row, offsetUnset) { - php_swoole_fatal_error(E_WARNING, "not supported"); - RETURN_FALSE; -} - -static PHP_METHOD(swoole_table_row, __destruct) {} 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 0b1915e81a3..1ab926a18a3 100644 --- a/ext-src/swoole_websocket_server.cc +++ b/ext-src/swoole_websocket_server.cc @@ -10,37 +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_http_server.h" -#include - SW_EXTERN_C_BEGIN -#include "ext/standard/url.h" #include "ext/standard/sha1.h" -#include "ext/standard/php_var.h" -#include "ext/standard/php_string.h" -#include "ext/date/php_date.h" -#include "main/php_variables.h" +#include "stubs/php_swoole_websocket_arginfo.h" SW_EXTERN_C_END #include "swoole_base64.h" -#include "thirdparty/swoole_http_parser.h" -using swoole::Server; using swoole::Connection; using swoole::ListenPort; -using swoole::String; using swoole::RecvData; -using swoole::coroutine::Socket; +using swoole::Server; using swoole::SessionId; +using swoole::String; +using swoole::coroutine::Socket; + +using HttpContext = swoole::http::Context; -using http_request = swoole::http::Request; -using http_response = swoole::http::Response; -using http_context = swoole::http::Context; +namespace WebSocket = swoole::websocket; zend_class_entry *swoole_websocket_server_ce; static zend_object_handlers swoole_websocket_server_handlers; @@ -51,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); @@ -62,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 @@ -118,7 +82,7 @@ static bool websocket_message_uncompress(String *buffer, const char *in, size_t #endif static void php_swoole_websocket_construct_frame(zval *zframe, zend_long opcode, zval *zpayload, uint8_t flags) { - if (opcode == WEBSOCKET_OPCODE_CLOSE) { + if (opcode == WebSocket::OPCODE_CLOSE) { const char *payload = Z_STRVAL_P(zpayload); size_t payload_length = Z_STRLEN_P(zpayload); object_init_ex(zframe, swoole_websocket_closeframe_ce); @@ -145,11 +109,11 @@ static void php_swoole_websocket_construct_frame(zval *zframe, zend_long opcode, zend_update_property_long(swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("flags"), flags); /* BC */ zend_update_property_bool( - swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("finish"), flags & SW_WEBSOCKET_FLAG_FIN); + swoole_websocket_frame_ce, SW_Z8_OBJ_P(zframe), ZEND_STRL("finish"), flags & WebSocket::FLAG_FIN); } void php_swoole_websocket_frame_unpack_ex(String *data, zval *zframe, uchar uncompress) { - swWebSocket_frame frame; + WebSocket::Frame frame; zval zpayload; uint8_t flags; @@ -159,19 +123,25 @@ void php_swoole_websocket_frame_unpack_ex(String *data, zval *zframe, uchar unco return; } - swWebSocket_decode(&frame, data); - flags = swWebSocket_get_flags(&frame); + 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; - flags ^= (SW_WEBSOCKET_FLAG_RSV1 | SW_WEBSOCKET_FLAG_COMPRESS); + frame.payload = zlib_buffer->str; + frame.payload_length = zlib_buffer->length; + flags ^= (WebSocket::FLAG_RSV1 | WebSocket::FLAG_COMPRESS); } #endif /* TODO: optimize memory copy */ @@ -207,51 +177,52 @@ static sw_inline int php_swoole_websocket_frame_pack_ex(String *buffer, } if (mask) { - flags |= SW_WEBSOCKET_FLAG_MASK; + flags |= WebSocket::FLAG_MASK; } #ifdef SW_HAVE_ZLIB - if (flags & SW_WEBSOCKET_FLAG_COMPRESS) { + if (flags & WebSocket::FLAG_COMPRESS) { if (!allow_compress) { - flags ^= SW_WEBSOCKET_FLAG_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; - flags |= SW_WEBSOCKET_FLAG_RSV1; + 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 swWebSocket_pack_close_frame(buffer, code, data, length, flags); + case WebSocket::OPCODE_CLOSE: + return WebSocket::pack_close_frame(buffer, code, data, length, flags); default: - swWebSocket_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( String *buffer, zval *zdata, zend_long opcode, uint8_t flags, zend_bool mask, zend_bool allow_compress) { return php_swoole_websocket_frame_pack_ex( - buffer, zdata, opcode, WEBSOCKET_CLOSE_NORMAL, flags, mask, allow_compress); + buffer, zdata, opcode, WebSocket::CLOSE_NORMAL, flags, mask, allow_compress); } int php_swoole_websocket_frame_object_pack_ex(String *buffer, zval *zdata, zend_bool mask, zend_bool allow_compress) { zval *zframe = zdata; - zend_long opcode = WEBSOCKET_OPCODE_TEXT; - zend_long code = WEBSOCKET_CLOSE_NORMAL; - zend_long flags = SW_WEBSOCKET_FLAG_FIN; + zend_long opcode = WebSocket::OPCODE_TEXT; + zend_long code = WebSocket::CLOSE_NORMAL; + zend_long flags = WebSocket::FLAG_FIN; 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) { + if (opcode == WebSocket::OPCODE_CLOSE) { if ((ztmp = sw_zend_read_property_not_null_ex( swoole_websocket_frame_ce, zframe, SW_ZSTR_KNOWN(SW_ZEND_STR_CODE), 1))) { code = zval_get_long(ztmp); @@ -262,37 +233,52 @@ 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))) { - flags = zval_get_long(ztmp) & SW_WEBSOCKET_FLAGS_ALL; + 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 |= SW_WEBSOCKET_FLAG_FIN; + flags |= WebSocket::FLAG_FIN; } else { - flags &= ~SW_WEBSOCKET_FLAG_FIN; + flags &= ~WebSocket::FLAG_FIN; } } return php_swoole_websocket_frame_pack_ex( - buffer, zdata, opcode, code, flags & SW_WEBSOCKET_FLAGS_ALL, mask, allow_compress); + buffer, zdata, opcode, code, flags & WebSocket::FLAGS_ALL, mask, allow_compress); } -void swoole_websocket_onOpen(Server *serv, http_context *ctx) { +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); } @@ -302,7 +288,7 @@ void swoole_websocket_onOpen(Server *serv, http_context *ctx) { /** * default onRequest callback */ -void swoole_websocket_onRequest(http_context *ctx) { +void swoole_websocket_onRequest(HttpContext *ctx) { const char *bad_request = "HTTP/1.1 400 Bad Request\r\n" "Connection: close\r\n" "Content-Type: text/html; charset=UTF-8\r\n" @@ -312,7 +298,7 @@ void swoole_websocket_onRequest(http_context *ctx) { "

HTTP 400 Bad Request


Powered by Swoole"; ctx->send(ctx, (char *) bad_request, strlen(bad_request)); - ctx->end = 1; + ctx->end_ = 1; ctx->close(ctx); } @@ -323,7 +309,7 @@ void php_swoole_sha1(const char *str, int _len, unsigned char *digest) { PHP_SHA1Final(digest, &context); } -bool swoole_websocket_handshake(http_context *ctx) { +bool swoole_websocket_handshake(HttpContext *ctx) { char sec_buf[128]; zval *header = ctx->request.zheader; HashTable *ht = Z_ARRVAL_P(header); @@ -333,7 +319,7 @@ bool swoole_websocket_handshake(http_context *ctx) { if (!(pData = zend_hash_str_find(ht, ZEND_STRL("sec-websocket-key")))) { _bad_request: ctx->response.status = SW_HTTP_BAD_REQUEST; - swoole_http_response_end(ctx, nullptr, &retval); + ctx->end(nullptr, &retval); return false; } @@ -352,15 +338,11 @@ bool swoole_websocket_handshake(http_context *ctx) { // base64 encode int sec_len = swoole::base64_encode((unsigned char *) sha1_str, sizeof(sha1_str), sec_buf); - swoole_http_response_set_header(ctx, ZEND_STRL("Upgrade"), ZEND_STRL("websocket"), false); - swoole_http_response_set_header(ctx, ZEND_STRL("Connection"), ZEND_STRL("Upgrade"), false); - swoole_http_response_set_header(ctx, ZEND_STRL("Sec-WebSocket-Accept"), sec_buf, sec_len, false); - swoole_http_response_set_header(ctx, ZEND_STRL("Sec-WebSocket-Version"), ZEND_STRL(SW_WEBSOCKET_VERSION), false); + ctx->set_header(ZEND_STRL("Upgrade"), ZEND_STRL("websocket"), false); + ctx->set_header(ZEND_STRL("Connection"), ZEND_STRL("Upgrade"), false); + 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; @@ -368,60 +350,34 @@ bool swoole_websocket_handshake(http_context *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; - swoole_http_response_set_header( - ctx, ZEND_STRL("Sec-Websocket-Extensions"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false); - } } -#endif if (conn) { - conn->websocket_status = WEBSOCKET_STATUS_ACTIVE; + conn->websocket_status = WebSocket::STATUS_ACTIVE; ListenPort *port = serv->get_port_by_server_fd(conn->server_fd); if (port && !port->websocket_subprotocol.empty()) { - swoole_http_response_set_header(ctx, - ZEND_STRL("Sec-WebSocket-Protocol"), - port->websocket_subprotocol.c_str(), - port->websocket_subprotocol.length(), - false); + ctx->set_header(ZEND_STRL("Sec-WebSocket-Protocol"), + port->websocket_subprotocol.c_str(), + 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; sock->protocol.package_length_size = SW_WEBSOCKET_HEADER_LEN; sock->protocol.package_length_offset = 0; sock->protocol.package_body_offset = 0; - sock->protocol.get_package_length = swWebSocket_get_package_length; -#ifdef SW_HAVE_ZLIB - ctx->websocket_compression = websocket_compression; -#endif + sock->protocol.get_package_length = WebSocket::get_package_length; } ctx->response.status = SW_HTTP_SWITCHING_PROTOCOLS; ctx->upgrade = 1; - swoole_http_response_end(ctx, nullptr, &retval); + ctx->end(nullptr, &retval); return Z_TYPE(retval) == IS_TRUE; } @@ -437,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; } @@ -469,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; @@ -491,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; } @@ -534,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; } @@ -553,7 +509,7 @@ int swoole_websocket_onMessage(Server *serv, RecvData *req) { SessionId fd = req->info.fd; uchar flags = 0; zend_long opcode = 0; - auto port = serv->get_port_by_session_id(fd); + auto port = serv->get_port_by_session_id(fd); if (!port) { return SW_ERR; } @@ -564,19 +520,19 @@ int swoole_websocket_onMessage(Server *serv, RecvData *req) { php_swoole_get_recv_data(serv, &zdata, req); - // frame info has already decoded in swWebSocket_dispatch_frame + // frame info has already decoded in websocket::dispatch_frame flags = frame_header[0]; opcode = frame_header[1]; - if ((opcode == WEBSOCKET_OPCODE_CLOSE && !port->open_websocket_close_frame) || - (opcode == WEBSOCKET_OPCODE_PING && !port->open_websocket_ping_frame) || - (opcode == WEBSOCKET_OPCODE_PONG && !port->open_websocket_pong_frame)) { - if (opcode == WEBSOCKET_OPCODE_PING) { + if ((opcode == WebSocket::OPCODE_CLOSE && !port->open_websocket_close_frame) || + (opcode == WebSocket::OPCODE_PING && !port->open_websocket_ping_frame) || + (opcode == WebSocket::OPCODE_PONG && !port->open_websocket_pong_frame)) { + if (opcode == WebSocket::OPCODE_PING) { String send_frame = {}; char buf[SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_CLOSE_CODE_LEN + SW_WEBSOCKET_CLOSE_REASON_MAX_LEN]; - send_frame.str = buf; + send_frame.str = buf; send_frame.size = sizeof(buf); - swWebSocket_encode(&send_frame, req->data, req->info.len, WEBSOCKET_OPCODE_PONG, SW_WEBSOCKET_FLAG_FIN); + WebSocket::encode(&send_frame, req->data, req->info.len, WebSocket::OPCODE_PONG, WebSocket::FLAG_FIN); serv->send(fd, send_frame.str, send_frame.length); } zval_ptr_dtor(&zdata); @@ -587,27 +543,27 @@ int swoole_websocket_onMessage(Server *serv, RecvData *req) { /** * RFC 7692 */ - if (serv->websocket_compression && (flags & SW_WEBSOCKET_FLAG_RSV1)) { - swoole_zlib_buffer->clear(); - if (!websocket_message_uncompress(swoole_zlib_buffer, Z_STRVAL(zdata), Z_STRLEN(zdata))) { + if (serv->websocket_compression && (flags & WebSocket::FLAG_RSV1)) { + 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); - flags ^= (SW_WEBSOCKET_FLAG_RSV1 | SW_WEBSOCKET_FLAG_COMPRESS); + 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); } @@ -618,7 +574,7 @@ int swoole_websocket_onMessage(Server *serv, RecvData *req) { return SW_OK; } -int swoole_websocket_onHandshake(Server *serv, ListenPort *port, http_context *ctx) { +int swoole_websocket_onHandshake(Server *serv, ListenPort *port, HttpContext *ctx) { SessionId fd = ctx->fd; bool success = swoole_websocket_handshake(ctx); if (success) { @@ -632,99 +588,116 @@ int swoole_websocket_onHandshake(Server *serv, ListenPort *port, http_context *c 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"), SW_WEBSOCKET_FLAG_FIN, 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); + swoole_websocket_closeframe_ce, ZEND_STRL("opcode"), WebSocket::OPCODE_CLOSE, ZEND_ACC_PUBLIC); zend_declare_property_long( - swoole_websocket_closeframe_ce, ZEND_STRL("code"), WEBSOCKET_CLOSE_NORMAL, ZEND_ACC_PUBLIC); + swoole_websocket_closeframe_ce, ZEND_STRL("code"), WebSocket::CLOSE_NORMAL, ZEND_ACC_PUBLIC); zend_declare_property_string(swoole_websocket_closeframe_ce, ZEND_STRL("reason"), "", ZEND_ACC_PUBLIC); /* {{{ swoole namespace */ // status - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_STATUS_CONNECTION", WEBSOCKET_STATUS_CONNECTION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_STATUS_HANDSHAKE", WEBSOCKET_STATUS_HANDSHAKE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_STATUS_ACTIVE", WEBSOCKET_STATUS_ACTIVE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_STATUS_CLOSING", WEBSOCKET_STATUS_CLOSING); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_STATUS_CONNECTION", WebSocket::STATUS_CONNECTION); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_STATUS_HANDSHAKE", WebSocket::STATUS_HANDSHAKE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_STATUS_ACTIVE", WebSocket::STATUS_ACTIVE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_STATUS_CLOSING", WebSocket::STATUS_CLOSING); // all opcodes - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_CONTINUATION", WEBSOCKET_OPCODE_CONTINUATION); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_TEXT", WEBSOCKET_OPCODE_TEXT); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_BINARY", WEBSOCKET_OPCODE_BINARY); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_CLOSE", WEBSOCKET_OPCODE_CLOSE); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_PING", WEBSOCKET_OPCODE_PING); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_PONG", WEBSOCKET_OPCODE_PONG); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_CONTINUATION", WebSocket::OPCODE_CONTINUATION); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_TEXT", WebSocket::OPCODE_TEXT); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_BINARY", WebSocket::OPCODE_BINARY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_CLOSE", WebSocket::OPCODE_CLOSE); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_PING", WebSocket::OPCODE_PING); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_OPCODE_PONG", WebSocket::OPCODE_PONG); // flags - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_FIN", SW_WEBSOCKET_FLAG_FIN); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_RSV1", SW_WEBSOCKET_FLAG_RSV1); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_RSV2", SW_WEBSOCKET_FLAG_RSV2); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_RSV3", SW_WEBSOCKET_FLAG_RSV3); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_MASK", SW_WEBSOCKET_FLAG_MASK); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_COMPRESS", SW_WEBSOCKET_FLAG_COMPRESS); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_FIN", WebSocket::FLAG_FIN); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_RSV1", WebSocket::FLAG_RSV1); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_RSV2", WebSocket::FLAG_RSV2); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_RSV3", WebSocket::FLAG_RSV3); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_MASK", WebSocket::FLAG_MASK); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_FLAG_COMPRESS", WebSocket::FLAG_COMPRESS); // close error - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_NORMAL", WEBSOCKET_CLOSE_NORMAL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_GOING_AWAY", WEBSOCKET_CLOSE_GOING_AWAY); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_PROTOCOL_ERROR", WEBSOCKET_CLOSE_PROTOCOL_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_DATA_ERROR", WEBSOCKET_CLOSE_DATA_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_STATUS_ERROR", WEBSOCKET_CLOSE_STATUS_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_ABNORMAL", WEBSOCKET_CLOSE_ABNORMAL); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_MESSAGE_ERROR", WEBSOCKET_CLOSE_MESSAGE_ERROR); - SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_POLICY_ERROR", WEBSOCKET_CLOSE_POLICY_ERROR); - 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_TLS", WEBSOCKET_CLOSE_TLS); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_NORMAL", WebSocket::CLOSE_NORMAL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_GOING_AWAY", WebSocket::CLOSE_GOING_AWAY); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_PROTOCOL_ERROR", WebSocket::CLOSE_PROTOCOL_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_DATA_ERROR", WebSocket::CLOSE_DATA_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_STATUS_ERROR", WebSocket::CLOSE_STATUS_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_ABNORMAL", WebSocket::CLOSE_ABNORMAL); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_MESSAGE_ERROR", WebSocket::CLOSE_MESSAGE_ERROR); + SW_REGISTER_LONG_CONSTANT("SWOOLE_WEBSOCKET_CLOSE_POLICY_ERROR", WebSocket::CLOSE_POLICY_ERROR); + 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 }}} */ /* BC */ // status - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_CONNECTION", WEBSOCKET_STATUS_CONNECTION); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_HANDSHAKE", WEBSOCKET_STATUS_HANDSHAKE); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_FRAME", WEBSOCKET_STATUS_ACTIVE); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_ACTIVE", WEBSOCKET_STATUS_ACTIVE); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_CLOSING", WEBSOCKET_STATUS_CLOSING); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_CONNECTION", WebSocket::STATUS_CONNECTION); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_HANDSHAKE", WebSocket::STATUS_HANDSHAKE); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_FRAME", WebSocket::STATUS_ACTIVE); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_ACTIVE", WebSocket::STATUS_ACTIVE); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_STATUS_CLOSING", WebSocket::STATUS_CLOSING); // all opcodes - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_CONTINUATION", WEBSOCKET_OPCODE_CONTINUATION); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_TEXT", WEBSOCKET_OPCODE_TEXT); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_BINARY", WEBSOCKET_OPCODE_BINARY); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_CLOSE", WEBSOCKET_OPCODE_CLOSE); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_PING", WEBSOCKET_OPCODE_PING); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_PONG", WEBSOCKET_OPCODE_PONG); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_CONTINUATION", WebSocket::OPCODE_CONTINUATION); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_TEXT", WebSocket::OPCODE_TEXT); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_BINARY", WebSocket::OPCODE_BINARY); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_CLOSE", WebSocket::OPCODE_CLOSE); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_PING", WebSocket::OPCODE_PING); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_OPCODE_PONG", WebSocket::OPCODE_PONG); // close error - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_NORMAL", WEBSOCKET_CLOSE_NORMAL); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_GOING_AWAY", WEBSOCKET_CLOSE_GOING_AWAY); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_PROTOCOL_ERROR", WEBSOCKET_CLOSE_PROTOCOL_ERROR); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_DATA_ERROR", WEBSOCKET_CLOSE_DATA_ERROR); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_STATUS_ERROR", WEBSOCKET_CLOSE_STATUS_ERROR); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_ABNORMAL", WEBSOCKET_CLOSE_ABNORMAL); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_MESSAGE_ERROR", WEBSOCKET_CLOSE_MESSAGE_ERROR); - SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_POLICY_ERROR", WEBSOCKET_CLOSE_POLICY_ERROR); - 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_TLS", WEBSOCKET_CLOSE_TLS); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_NORMAL", WebSocket::CLOSE_NORMAL); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_GOING_AWAY", WebSocket::CLOSE_GOING_AWAY); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_PROTOCOL_ERROR", WebSocket::CLOSE_PROTOCOL_ERROR); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_DATA_ERROR", WebSocket::CLOSE_DATA_ERROR); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_STATUS_ERROR", WebSocket::CLOSE_STATUS_ERROR); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_ABNORMAL", WebSocket::CLOSE_ABNORMAL); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_MESSAGE_ERROR", WebSocket::CLOSE_MESSAGE_ERROR); + SW_REGISTER_LONG_CONSTANT("WEBSOCKET_CLOSE_POLICY_ERROR", WebSocket::CLOSE_POLICY_ERROR); + 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) { @@ -734,7 +707,7 @@ static sw_inline bool swoole_websocket_server_push(Server *serv, SessionId fd, S } Connection *conn = serv->get_connection_by_session_id(fd); - if (!conn || conn->websocket_status < WEBSOCKET_STATUS_HANDSHAKE) { + if (!conn || conn->websocket_status < WebSocket::STATUS_HANDSHAKE) { swoole_set_last_error(SW_ERROR_WEBSOCKET_UNCONNECTED); php_swoole_fatal_error( E_WARNING, "the connected client of connection[%ld] is not a websocket client or closed", fd); @@ -750,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; } @@ -762,7 +736,7 @@ static sw_inline bool swoole_websocket_server_close(Server *serv, SessionId fd, Connection *conn = serv->get_connection_by_session_id(fd); if (conn) { // Change status immediately to avoid double close - conn->websocket_status = WEBSOCKET_STATUS_CLOSING; + conn->websocket_status = WebSocket::STATUS_CLOSING; // Server close connection immediately return serv->close(fd, false); } else { @@ -778,18 +752,22 @@ static PHP_METHOD(swoole_websocket_server, disconnect) { } zend_long fd = 0; - zend_long code = WEBSOCKET_CLOSE_NORMAL; + zend_long code = WebSocket::CLOSE_NORMAL; 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 (swWebSocket_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) { @@ -801,9 +779,9 @@ static PHP_METHOD(swoole_websocket_server, push) { zend_long fd = 0; zval *zdata = nullptr; - zend_long opcode = WEBSOCKET_OPCODE_TEXT; + zend_long opcode = WebSocket::OPCODE_TEXT; zval *zflags = nullptr; - zend_long flags = SW_WEBSOCKET_FLAG_FIN; + zend_long flags = WebSocket::FLAG_FIN; #ifdef SW_HAVE_ZLIB zend_bool allow_compress = 0; #endif @@ -823,38 +801,60 @@ 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 & SW_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 & SW_WEBSOCKET_FLAG_FIN)); + case WebSocket::OPCODE_CLOSE: + 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; + zend_long opcode = WebSocket::OPCODE_TEXT; zval *zflags = nullptr; - zend_long flags = SW_WEBSOCKET_FLAG_FIN; + zend_long flags = WebSocket::FLAG_FIN; ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_ZVAL(zdata) @@ -867,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 & SW_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) { @@ -893,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); } @@ -907,15 +916,15 @@ static PHP_METHOD(swoole_websocket_server, isEstablished) { RETURN_FALSE; } - zend_long fd; + zend_long session_id; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &fd) == 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_by_session_id(fd); + Connection *conn = serv->get_connection_verify(session_id); // not isEstablished - if (!conn || conn->active == 0 || conn->closed || conn->websocket_status < WEBSOCKET_STATUS_ACTIVE) { + if (!conn || conn->closed || conn->websocket_status < WebSocket::STATUS_ACTIVE) { RETURN_FALSE; } else { RETURN_TRUE; diff --git a/gdbinit b/gdbinit index 4c5d34eeb36..2c47fc1605e 100644 --- a/gdbinit +++ b/gdbinit @@ -58,10 +58,10 @@ define sw_hash_map_list end define co_list - call swoole_coro_iterator_reset() + call swoole_coroutine_iterator_reset() set $running = 1 while $running - set $co = swoole_coro_iterator_each() + set $co = swoole_coroutine_iterator_each() if $co printf "coroutine %ld ", $co->cid if $co->state == 0 @@ -83,7 +83,7 @@ define co_list end define co_bt - if swoole_coro_count() == 0 + if swoole_coroutine_count() == 0 printf "no coroutine is running\n" end ____sw_executor_globals @@ -111,7 +111,7 @@ end define __co_bt set $cid = (int)$arg0 - set $co = swoole_coro_get($cid) + set $co = swoole_coroutine_get($cid) if $co set $task = ('swoole::PHPContext' *) $co->get_task() if $task @@ -125,7 +125,7 @@ end define co_status printf "\t c_stack_size: %d\n", 'swoole::Coroutine::stack_size' printf "\t active: %d\n", 'swoole::PHPCoroutine::active' - printf "\t coro_num: %d\n", swoole_coro_count() + printf "\t coro_num: %d\n", swoole_coroutine_count() printf "\t peak_coro_num: %d\n", 'swoole::Coroutine::peak_num' printf "\t config: " print 'swoole::PHPCoroutine::config' 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 df1122805b2..4f19d095cff 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -10,16 +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 | | Twosee | +----------------------------------------------------------------------+ */ #pragma once -#if defined(HAVE_CONFIG_H) && !defined(COMPILE_DL_SWOOLE) +#ifdef HAVE_CONFIG_H #include "config.h" -#elif defined(PHP_ATOM_INC) || defined(ZEND_SIGNALS) +#elif defined(ENABLE_PHP_SWOOLE) #include "php_config.h" #endif @@ -35,10 +35,15 @@ #define _GNU_SOURCE #endif +#ifndef _PTHREAD_PSHARED +#define _PTHREAD_PSHARED +#endif + /*--- C standard library ---*/ #include #include #include +#include #include #include #include @@ -51,8 +56,11 @@ #include #include +#include #include +#include #include +#include typedef unsigned long ulong_t; @@ -102,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 @@ -126,6 +146,7 @@ typedef unsigned long ulong_t; #define SW_MAX(A, B) ((A) > (B) ? (A) : (B)) #define SW_MIN(A, B) ((A) < (B) ? (A) : (B)) #define SW_LOOP_N(n) for (decltype(n) i = 0; i < n; i++) +#define SW_LOOP for (;;) #ifndef MAX #define MAX(A, B) SW_MAX(A, B) @@ -134,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) @@ -151,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); @@ -175,16 +210,25 @@ 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; +} struct Protocol; struct EventData; 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; @@ -195,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-------------------------------------*/ @@ -206,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 @@ -219,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) @@ -265,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); } @@ -292,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, @@ -332,20 +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_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 @@ -355,20 +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 */ @@ -377,38 +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_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() @@ -421,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) { @@ -438,55 +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_END = 1u << 3, - SW_EVENT_DATA_OBJ_PTR = 1u << 4, - SW_EVENT_DATA_POP_PTR = 1u << 5, + 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, }; @@ -508,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); @@ -523,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(clock_id_t 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); @@ -551,48 +627,78 @@ 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; - struct DataHead { SessionId fd; + uint64_t msg_id; uint32_t len; int16_t reactor_id; uint8_t type; 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; - uint8_t aio_init; - uint8_t aio_schedule; - uint32_t aio_task_num; - Pipe *aio_pipe; - network::Socket *aio_read_socket; - network::Socket *aio_write_socket; + 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; @@ -605,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; @@ -623,7 +756,7 @@ struct Global { int signal_fd; bool signal_alarm; - uint32_t trace_flags; + long trace_flags; void (*fatal_error)(int code, const char *str, ...); @@ -637,28 +770,39 @@ struct Global { Allocator std_allocator; std::string task_tmpfile; //-----------------------[DNS]-------------------------- - char *dns_server_v4; - char *dns_server_v6; + std::string dns_server_host; + int dns_server_port; 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; - swoole::network::Socket *aio_default_socket; + 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); int hook_add(void **hooks, int type, const Callback &func, int push_back); void hook_call(void **hooks, int type, void *arg); 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) @@ -666,24 +810,85 @@ static inline void swoole_set_last_error(int error) { SwooleTG.error = error; } -static inline int swoole_get_last_error() { +static inline int swoole_get_last_error(void) { return SwooleTG.error; } -static inline int swoole_get_thread_id() { +static inline void swoole_clear_last_error(void) { + SwooleTG.error = 0; +} + +void swoole_clear_last_error_msg(void); +const char *swoole_get_last_error_msg(void); + +static inline int swoole_get_thread_id(void) { return SwooleTG.id; } -static inline int swoole_get_process_type() { - return SwooleG.process_type; +static inline int swoole_get_thread_type(void) { + return SwooleTG.type; +} + +static inline void swoole_set_thread_id(uint16_t id) { + SwooleTG.id = id; } -static inline int swoole_get_process_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_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) { @@ -708,7 +913,7 @@ static sw_inline void sw_spinlock(sw_atomic_t *lock) { } static sw_inline swoole::String *sw_tg_buffer() { - return SwooleTG.buffer_stack; + return SwooleTG.buffer_stack; } static sw_inline swoole::MemoryPool *sw_mem_pool() { diff --git a/include/swoole_api.h b/include/swoole_api.h index a71b87c355f..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); @@ -36,19 +42,23 @@ SW_API swoole::TimerNode *swoole_timer_get(long timer_id); SW_API bool swoole_timer_clear(long timer_id); SW_API void swoole_timer_free(); SW_API int swoole_timer_select(); +SW_API bool swoole_timer_is_available(); SW_API int swoole_event_init(int flags); SW_API int swoole_event_add(swoole::network::Socket *socket, int events); SW_API int swoole_event_set(swoole::network::Socket *socket, int events); +SW_API int swoole_event_add_or_update(swoole::network::Socket *socket, int event); 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 5c351aaa42a..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 | +----------------------------------------------------------------------+ */ @@ -25,31 +25,29 @@ #define O_DIRECT 040000 #endif -enum flag { +namespace swoole { + +enum AsyncFlag { SW_AIO_WRITE_FSYNC = 1u << 1, SW_AIO_EOF = 1u << 2, }; -namespace swoole { +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 ret; - int error; + ssize_t retval; /** * internal use only */ @@ -58,6 +56,73 @@ struct AsyncEvent { void *object; void (*handler)(AsyncEvent *event); void (*callback)(AsyncEvent *event); + + bool catch_error() { + return (error == SW_ERROR_AIO_TIMEOUT || error == SW_ERROR_AIO_CANCELED); + } +}; + +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: + size_t task_num = 0; + Pipe *pipe = nullptr; + std::shared_ptr pool; + network::Socket *read_socket = nullptr; + network::Socket *write_socket = nullptr; + + AsyncThreads(); + ~AsyncThreads(); + + 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); }; namespace async { @@ -65,13 +130,6 @@ namespace async { typedef void (*Handler)(AsyncEvent *event); AsyncEvent *dispatch(const AsyncEvent *request); -int cancel(int task_id); -int callback(Reactor *reactor, swEvent *_event); -size_t thread_count(); - -#ifdef SW_DEBUG -void notify_one(); -#endif void handler_gethostbyname(AsyncEvent *event); void handler_getaddrinfo(AsyncEvent *event); diff --git a/include/swoole_atomic.h b/include/swoole_atomic.h index 8349d22bf36..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,39 +19,89 @@ typedef volatile int32_t sw_atomic_int32_t; typedef volatile uint32_t sw_atomic_uint32_t; - -#ifdef __x86_64__ typedef volatile int64_t sw_atomic_int64_t; typedef volatile uint64_t sw_atomic_uint64_t; -#endif -#ifdef __x86_64__ 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") #else #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 12b69fe1a3f..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; @@ -49,6 +47,7 @@ class Client { bool remove_delay = false; bool closed = false; bool high_watermark = false; + bool async_connect = false; /** * one package: length check @@ -104,12 +103,12 @@ class Client { std::shared_ptr ssl_context = nullptr; #endif - void (*onConnect)(Client *cli) = nullptr; - void (*onError)(Client *cli) = nullptr; - void (*onReceive)(Client *cli, const char *data, uint32_t length) = nullptr; - void (*onClose)(Client *cli) = nullptr; - void (*onBufferFull)(Client *cli) = nullptr; - void (*onBufferEmpty)(Client *cli) = 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; @@ -117,9 +116,32 @@ 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) { + http_proxy = new swoole::HttpProxy; + http_proxy->proxy_host = host; + http_proxy->proxy_port = port; + } + + Socket *get_socket() { + 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); @@ -128,6 +150,9 @@ class Client { int socks5_handshake(const char *recv_data, size_t length); #ifdef SW_USE_OPENSSL int enable_ssl_encrypt(); +#ifdef SW_SUPPORT_DTLS + void enable_dtls(); +#endif int ssl_handshake(); int ssl_verify(int allow_self_signed); #endif @@ -150,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; @@ -160,11 +185,11 @@ class Stream { } } ~Stream(); - static int recv_blocking(Socket *sock, void *__buf, size_t __len); + static ssize_t recv_blocking(Socket *sock, void *__buf, size_t __len); 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------------------------------------ @@ -174,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; } @@ -185,13 +210,23 @@ class SyncClient { if (connected || !created) { return false; } - if (client.connect(&client, host, port, timeout, 0) < 0) { + if (client.connect(&client, host, port, timeout, client.socket->is_dgram()) < 0) { return false; } connected = true; return true; } +#ifdef SW_USE_OPENSSL + bool enable_ssl_encrypt() { + if (client.enable_ssl_encrypt() < 0 || client.ssl_handshake() < 0) { + return false; + } else { + return true; + } + } +#endif + ssize_t send(const std::string &data) { return client.send(&client, data.c_str(), data.length(), 0); } @@ -225,10 +260,10 @@ class AsyncClient : public SyncClient { std::function _onConnect = nullptr; std::function _onError = nullptr; std::function _onClose = nullptr; - std::function _onReceive = nullptr; + 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; @@ -244,7 +279,7 @@ class AsyncClient : public SyncClient { AsyncClient *ac = (AsyncClient *) cli->object; ac->_onClose(ac); }; - client.onReceive = [](Client *cli, const char *data, uint32_t length) { + client.onReceive = [](Client *cli, const char *data, size_t length) { AsyncClient *ac = (AsyncClient *) cli->object; ac->_onReceive(ac, data, length); }; diff --git a/include/swoole_config.h b/include/swoole_config.h index 7b6bd0a7e7d..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,34 +147,33 @@ #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" #define SW_DNS_HOST_BUFFER_SIZE 16 #define SW_DNS_SERVER_PORT 53 -#define SW_DNS_DEFAULT_SERVER "8.8.8.8" +#define SW_DNS_RESOLV_CONF "/etc/resolv.conf" #define SW_Z_BEST_SPEED 1 +#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 @@ -218,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" @@ -270,8 +252,7 @@ * Coroutine */ #define SW_DEFAULT_C_STACK_SIZE (2 * 1024 * 1024) -#define SW_CORO_SUPPORT_BAILOUT 1 -#define SW_CORO_SWAP_BAILOUT 1 +#define SW_CORO_BAILOUT_EXIT_CODE 1 //#define SW_CONTEXT_PROTECT_STACK_PAGE 1 //#define SW_CONTEXT_DETECT_STACK_USAGE 1 diff --git a/include/swoole_coroutine.h b/include/swoole_coroutine.h index 062cbe5641e..3749fd65bb9 100644 --- a/include/swoole_coroutine.h +++ b/include/swoole_coroutine.h @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | | Twosee | +----------------------------------------------------------------------+ */ @@ -23,24 +23,30 @@ #include "swoole_reactor.h" #include "swoole_timer.h" #include "swoole_async.h" +#include "swoole_util.h" #include "swoole_coroutine_context.h" -#include +#include #include #include #include +#ifdef SW_USE_THREAD_CONTEXT +#include +#endif + +typedef std::chrono::microseconds seconds_type; + +#ifdef SW_CORO_TIME +#define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine) calc_execute_usec(yield_coroutine, resume_coroutine) +#else +#define CALC_EXECUTE_USEC(yield_coroutine, resume_coroutine) +#endif namespace swoole { class Coroutine { public: - void resume(); - void yield(); - - void resume_naked(); - void yield_naked(); - constexpr static int STACK_ALIGNED_SIZE = (4 * 1024); constexpr static int MIN_STACK_SIZE = (64 * 1024); constexpr static int MAX_STACK_SIZE = (16 * 1024 * 1024); @@ -59,59 +65,117 @@ class Coroutine { ERR_INVALID = -2, }; + enum ResumeCode { + RC_OK = 0, + RC_TIMEDOUT = -1, + RC_CANCELED = -2, + }; + typedef void (*SwapCallback)(void *); - typedef void (*BailoutCallback)(); + typedef std::function BailoutCallback; + typedef std::function CancelFunc; + + void resume(); + void yield(); + void yield(CancelFunc *cancel_fn); + bool cancel(); - inline enum State get_state() { + bool yield_ex(double timeout = -1); + + enum State get_state() const { return state; } - inline long get_init_msec() { + long get_init_msec() const { return init_msec; } - inline long get_cid() { + 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(); } - inline void set_task(void *_task) { + bool is_canceled() const { + return resume_code_ == RC_CANCELED; + } + + bool is_timedout() const { + return resume_code_ == RC_TIMEDOUT; + } + + bool is_suspending() const { + return state == STATE_WAITING; + } + + void set_task(void *_task) { task = _task; } - static std::unordered_map coroutines; + void set_cancel_fn(CancelFunc *cancel_fn) { + cancel_fn_ = cancel_fn; + } + + 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 long create(const coroutine_func_t &fn, void *args = nullptr) { + 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) { + swoole_set_last_error(e.code().value()); + swoole_warning("failed to create coroutine, Error: %s[%d]", e.what(), swoole_get_last_error()); + return -1; + } +#else return (new Coroutine(fn, args))->run(); +#endif } + static void activate(); + static void deactivate(); + static inline Coroutine *get_current() { return current; } 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; } @@ -159,50 +223,78 @@ class Coroutine { return sw_likely(co) ? Timer::get_absolute_msec() - co->get_init_msec() : -1; } + static inline long get_execute_time(long cid) { + Coroutine *co = cid == 0 ? get_current() : get_by_cid(cid); + return sw_likely(co) ? co->get_execute_usec() : -1; + } + + static inline void calc_execute_usec(Coroutine *yield_coroutine, Coroutine *resume_coroutine) { + long current_usec = time(true); + if (yield_coroutine) { + yield_coroutine->execute_usec += current_usec - yield_coroutine->switch_usec; + } + + if (resume_coroutine) { + resume_coroutine->switch_usec = current_usec; + } + } + static void print_list(); protected: - static Coroutine *current; - static long last_cid; - static uint64_t peak_num; - static size_t stack_size; - static SwapCallback on_yield; /* before yield */ - static SwapCallback on_resume; /* before resume */ - static SwapCallback on_close; /* before close */ - static BailoutCallback on_bailout; /* when bailout */ + static 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 coroutine_func_t &fn, void *private_data) : ctx(stack_size, fn, private_data) { + Coroutine(const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { cid = ++last_cid; coroutines[cid] = this; if (sw_unlikely(count() > peak_num)) { peak_num = count(); } + if (!activated) { + activate(); + } + } + + Coroutine(long _cid, const CoroutineFunc &fn, void *private_data) : ctx(stack_size, fn, private_data) { + cid = _cid; } - inline long run() { + long run() { long cid = this->cid; origin = current; current = this; + CALC_EXECUTE_USEC(origin, nullptr); + state = STATE_RUNNING; ctx.swap_in(); check_end(); return cid; } - inline void check_end() { + void check_end() { if (ctx.is_end()) { close(); } else if (sw_unlikely(on_bailout)) { SW_ASSERT(current == nullptr); on_bailout(); - // expect that never here - exit(1); } } @@ -210,9 +302,25 @@ class Coroutine { }; //------------------------------------------------------------------------------- namespace coroutine { +/** + * Support for timeouts and cancellations requires the caller to store the memory pointers of + * the input and output parameter objects in the `data` pointer of the `AsyncEvent` object. + * This field is a `shared_ptr`, which increments the reference count when dispatched to the AIO thread, + * collectively managing the `data` pointer. + * When the async task is completed, the caller receives the results or cancels or timeouts, + * the reference count will reach zero, and the memory will be released. + */ bool async(async::Handler handler, AsyncEvent &event, double timeout = -1); -bool async(const std::function &fn, double timeout = -1); -bool run(const coroutine_func_t &fn, void *arg = nullptr); +/** + * 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 @@ -220,7 +328,7 @@ bool run(const coroutine_func_t &fn, void *arg = nullptr); /** * for gdb */ -swoole::Coroutine *swoole_coro_iterator_each(); -void swoole_coro_iterator_reset(); -swoole::Coroutine *swoole_coro_get(long cid); -size_t swoole_coro_count(); +swoole::Coroutine *swoole_coroutine_iterator_each(); +void swoole_coroutine_iterator_reset(); +swoole::Coroutine *swoole_coroutine_get(long cid); +size_t swoole_coroutine_count(); diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index d8f9e08e5bd..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 | +----------------------------------------------------------------------+ */ @@ -27,15 +27,20 @@ extern "C" { #include #include #include +#include #include #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); /** @@ -47,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); @@ -55,6 +62,41 @@ 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 + */ +FILE *swoole_coroutine_fopen(const char *pathname, const char *mode); +FILE *swoole_coroutine_fdopen(int fd, const char *mode); +FILE *swoole_coroutine_freopen(const char *pathname, const char *mode, FILE *stream); +size_t swoole_coroutine_fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +size_t swoole_coroutine_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); +char *swoole_coroutine_fgets(char *s, int size, FILE *stream); +int swoole_coroutine_fputs(const char *s, FILE *stream); +int swoole_coroutine_feof(FILE *stream); +int swoole_coroutine_fclose(FILE *stream); /** * dir */ @@ -65,6 +107,9 @@ int swoole_coroutine_closedir(DIR *dirp); * socket */ 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); ssize_t swoole_coroutine_recv(int sockfd, void *buf, size_t len, int flags); @@ -72,7 +117,9 @@ 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); int swoole_coroutine_getaddrinfo(const char *name, const char *service, @@ -82,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 b5b85fc8c24..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,14 +32,21 @@ namespace coroutine { //------------------------------------------------------------------------------- class Channel { public: - enum opcode { + enum Opcode { PRODUCER = 1, CONSUMER = 2, }; + enum ErrorCode { + ERROR_OK = 0, + ERROR_TIMEOUT = -1, + ERROR_CLOSED = -2, + ERROR_CANCELED = -3, + }; + struct TimeoutMessage { Channel *chan; - enum opcode type; + Opcode type; Coroutine *co; bool error; TimerNode *timer; @@ -66,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; } @@ -99,36 +106,41 @@ class Channel { return data; } + int get_error() { + return error_; + } + protected: size_t capacity = 1; bool closed = false; + int error_ = 0; std::list producer_queue; std::list consumer_queue; std::queue data_queue; 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 c956dd03888..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 | +----------------------------------------------------------------------+ */ @@ -24,6 +24,9 @@ #include #elif !defined(SW_USE_ASM_CONTEXT) #define USE_UCONTEXT 1 +#ifndef _XOPEN_SOURCE +#define _XOPEN_SOURCE +#endif #include #else #define USE_ASM_CONTEXT 1 @@ -39,29 +42,42 @@ 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 coroutine_func_t; +typedef std::function CoroutineFunc; namespace swoole { namespace coroutine { class Context { public: - Context(size_t stack_size, const coroutine_func_t &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_; } - static void context_func(void *arg); protected: - coroutine_func_t fn_; + CoroutineFunc fn_; #ifdef SW_USE_THREAD_CONTEXT std::thread thread_; std::mutex lock_; @@ -77,6 +93,8 @@ class Context { #endif void *private_data_; bool end_; + + 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 925489b6df6..f5eff4a307a 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -10,14 +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 | | Twosee | +----------------------------------------------------------------------+ */ #pragma once +#include "swoole.h" #include "swoole_api.h" +#include "swoole_socket.h" #include "swoole_coroutine.h" #include "swoole_protocol.h" #include "swoole_proxy.h" @@ -60,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_connect() { - 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(); @@ -78,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()); } @@ -93,9 +103,10 @@ class Socket { ssize_t recv_all(void *__buf, size_t __n); ssize_t send_all(const void *__buf, size_t __n); ssize_t recv_packet(double timeout = 0); + 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 { @@ -103,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); @@ -118,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()) { @@ -137,68 +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; @@ -212,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) { @@ -227,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; @@ -260,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; } @@ -278,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; @@ -290,21 +297,23 @@ class Socket { return connect_timeout; } else if (type == TIMEOUT_READ) { return read_timeout; - } else // if (type == TIMEOUT_WRITE) - { + } else if (type == TIMEOUT_WRITE) { return write_timeout; + } else { + assert(0); + return -1; } } - 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) { @@ -314,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) { @@ -324,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; } @@ -333,7 +350,7 @@ class Socket { return tmp; } - inline String *pop_write_buffer() { + String *pop_write_buffer() { if (sw_unlikely(!write_buffer)) { return nullptr; } @@ -342,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; } @@ -370,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; @@ -380,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; @@ -411,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); @@ -428,11 +459,19 @@ 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); - inline void init_options() { + void check_return_value(ssize_t retval) { + if (retval >= 0) { + set_err(0); + } else if (errCode == 0) { + set_err(errno); + } + } + + void init_options() { if (type == SW_SOCK_TCP || type == SW_SOCK_TCP6) { set_option(IPPROTO_TCP, TCP_NODELAY, 1); } @@ -443,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; @@ -465,18 +509,16 @@ 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; - } else // if (timeout < 0) - { - *timer_pp = (TimerNode *) -1; } + *timer_pp = (TimerNode *) -1; } return true; } @@ -500,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]); } } } @@ -534,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(); @@ -549,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; @@ -566,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; @@ -582,9 +626,14 @@ class ProtocolSwitch { } }; -std::vector dns_lookup(const char *domain, double timeout = 2.0); +std::vector dns_lookup(const char *domain, int family = AF_INET, double timeout = 2.0); +std::vector dns_lookup_impl_with_socket(const char *domain, int family, double timeout); +#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 9404db8d1a8..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,11 +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 f5fe0bc2757..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,15 +34,27 @@ 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, SW_ERROR_FILE_EMPTY, - SW_ERROR_DNSLOOKUP_DUPLICATE_REQUEST, + + SW_ERROR_DNSLOOKUP_DUPLICATE_REQUEST = 710, SW_ERROR_DNSLOOKUP_RESOLVE_FAILED, SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT, - SW_ERROR_BAD_IPV6_ADDRESS, + SW_ERROR_DNSLOOKUP_UNSUPPORTED, + SW_ERROR_DNSLOOKUP_NO_SERVER, + + SW_ERROR_BAD_IPV6_ADDRESS = 720, SW_ERROR_UNREGISTERED_SIGNAL, + SW_ERROR_BAD_HOST_ADDR, // EventLoop SW_ERROR_EVENT_SOCKET_REMOVED = 800, @@ -68,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 @@ -87,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 @@ -119,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 @@ -139,11 +160,18 @@ 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, /** * Coroutine @@ -165,5 +193,24 @@ enum swErrorCode { SW_ERROR_CO_STD_THREAD_LINK_ERROR, SW_ERROR_CO_DISABLED_MULTI_THREAD, + SW_ERROR_CO_CANNOT_CANCEL, + SW_ERROR_CO_NOT_EXISTS, + 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 }; + +namespace swoole { +class Exception { + public: + int code; + const char *msg; + + 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 5c097e6637c..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,17 +19,52 @@ #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 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) +#define feof(stream) swoole_coroutine_feof(stream) +#define fclose(stream) swoole_coroutine_fclose(stream) #define opendir(name) swoole_coroutine_opendir(name) #define readdir(dir) swoole_coroutine_readdir(dir) 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 f84ee666731..94a3dbdabdd 100644 --- a/include/swoole_http.h +++ b/include/swoole_http.h @@ -10,19 +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, @@ -57,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, @@ -91,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, @@ -101,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; @@ -121,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 d34550a7371..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,33 +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", \ - swHttp2_get_type_color(type), \ - swHttp2_get_type(type), \ - length, \ - flags, \ - stream_id, \ - ##__VA_ARGS__, \ - swHttp2FrameTraceLogFlags); - -struct swHttp2_settings { +#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; }; @@ -129,7 +131,7 @@ struct swHttp2_settings { | Frame Payload (0...) ... +---------------------------------------------------------------+ */ -struct swHttp2_frame { +struct Frame { uint32_t length : 24; uint32_t type : 8; uint32_t flags : 8; @@ -138,21 +140,49 @@ struct swHttp2_frame { char data[0]; }; -static sw_inline ssize_t swHttp2_get_length(const char *buf) { +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 swHttp2_get_frame_length(swProtocol *protocol, swSocket *conn, const char *buf, uint32_t length); -int swHttp2_send_setting_frame(swProtocol *protocol, swSocket *conn); -const char *swHttp2_get_type(int type); -int swHttp2_get_type_color(int type); - -static sw_inline void swHttp2_init_settings(swHttp2_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; +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 = 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"; + } } /** @@ -166,8 +196,7 @@ static sw_inline void swHttp2_init_settings(swHttp2_settings *settings) { | Frame Payload (0...) ... +---------------------------------------------------------------+ */ -static sw_inline void swHttp2_set_frame_header( - char *buffer, uint8_t type, uint32_t length, uint8_t flags, uint32_t stream_id) { +static sw_inline void set_frame_header(char *buffer, uint8_t type, uint32_t length, uint8_t flags, uint32_t stream_id) { buffer[0] = length >> 16; buffer[1] = length >> 8; buffer[2] = length; @@ -175,3 +204,6 @@ static sw_inline void swHttp2_set_frame_header( buffer[4] = flags; *(uint32_t *) (buffer + 5) = htonl(stream_id); } + +} // namespace http2 +} // namespace swoole 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 d1bb4580d9b..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, @@ -76,6 +76,7 @@ class Logger { bool redirect_stdout_and_stderr(int enable); void set_date_with_microseconds(bool enable); std::string gen_real_file(const std::string &file); + static std::string get_pretty_name(const std::string &prettyFunction, bool strip = true); void display_backtrace() { display_backtrace_ = true; @@ -84,27 +85,28 @@ class Logger { } // namespace swoole 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]", \ - __func__, \ + __SW_FUNC__, \ __LINE__, \ ##__VA_ARGS__, \ swoole_strerror(errno), \ @@ -113,23 +115,22 @@ swoole::Logger *sw_logger(); } \ } while (0) -#define swWarn(str, ...) \ +#define swoole_warning(str, ...) \ do { \ - if (SW_LOG_WARNING >= sw_logger()->get_level()) { \ - size_t _sw_error_len = sw_snprintf(sw_error, SW_ERROR_MSG_SIZE, "%s: " str, __func__, ##__VA_ARGS__); \ + 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(:%d): " str ", Error: %s[%d]", \ - __func__, \ - __LINE__, \ + "%s(): " str ", Error: %s[%d]", \ + __SW_FUNC__, \ ##__VA_ARGS__, \ swoole_strerror(errno), \ errno); \ @@ -137,56 +138,51 @@ 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, \ - "%s(:%d): " str ", Error: %s[%d]", \ - __func__, \ - __LINE__, \ + "%s(): " str ", Error: %s[%d]", \ + __SW_FUNC__, \ ##__VA_ARGS__, \ 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__); \ - abort(); \ - } while (0) +#define swoole_fatal_error(code, str, ...) SwooleG.fatal_error(code, str, ##__VA_ARGS__) -#define swoole_error_log(level, __errno, str, ...) \ +#define swoole_error_log(level, error, str, ...) \ do { \ - swoole_set_last_error(__errno); \ - if (level >= sw_logger()->get_level()) { \ + swoole_set_last_error(error); \ + 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, __func__, __errno, ##__VA_ARGS__); \ + 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); \ } \ } 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, __func__, __LINE__, ##__VA_ARGS__); \ + 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 */ @@ -222,7 +218,7 @@ enum swTrace_type { SW_TRACE_EOF_PROTOCOL = 1u << 12, SW_TRACE_LENGTH_PROTOCOL = 1u << 13, SW_TRACE_CLOSE = 1u << 14, - SW_TRACE_WEBSOCEKT = 1u << 15, + SW_TRACE_WEBSOCKET = 1u << 15, /** * Client */ @@ -242,19 +238,27 @@ enum swTrace_type { SW_TRACE_CONTEXT = 1u << 26, SW_TRACE_CO_HTTP_SERVER = 1u << 27, SW_TRACE_TABLE = 1u << 28, + 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 = 0xffffffff + 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, __func__, __LINE__, ##__VA_ARGS__); \ + 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 eb40222abc2..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(); }; @@ -76,6 +79,8 @@ class GlobalMemory : public MemoryPool { void *alloc(uint32_t size); 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 f1bdd7d9a28..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 | +----------------------------------------------------------------------+ */ @@ -20,18 +20,21 @@ #include "swoole.h" #include +#include #include "swoole_lock.h" #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, @@ -40,40 +43,90 @@ 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_; + int status_; + + public: + ExitStatus(pid_t _pid, int _status) : pid_(_pid), status_(_status) {} + + pid_t get_pid() const { + return pid_; + } + + int get_status() const { + return status_; + } + + int get_code() const { + return WEXITSTATUS(status_); + } + + int get_signal() const { + return WTERMSIG(status_); + } + + bool is_normal_exit() { + return WIFEXITED(status_); + } +}; + +static inline ExitStatus wait_process() { + int status = 0; + pid_t pid = ::wait(&status); + return ExitStatus(pid, status); +} + +static inline ExitStatus wait_process(pid_t _pid, int options) { + int status = 0; + pid_t pid = ::waitpid(_pid, &status, options); + return ExitStatus(pid, status); +} + struct ProcessPool; struct Worker; struct WorkerGlobal { - /** - * Always run - */ - bool run_always; bool shutdown; + bool running; + uint32_t max_request; /** - * pipe_worker + * 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. */ - int pipe_used; - - uint32_t max_request; - - String **output_buffer; 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; @@ -84,26 +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; - - /** - * worker id - */ - uint32_t id; + sw_atomic_long_t dispatch_count; + sw_atomic_long_t request_count; + sw_atomic_long_t response_count; + size_t coroutine_num; Mutex *lock; - UnixSocket *pipe_object; network::Socket *pipe_master; @@ -114,12 +159,48 @@ struct Worker { void *ptr2; ssize_t send_pipe_message(const void *buf, size_t n, int flags); + bool has_exceeded_max_request(); + void set_max_request(uint32_t max_request, uint32_t max_request_grace); + void report_error(const ExitStatus &exit_status); + /** + * Init global state for worker process. + * Must be called after the process is spawned and before the main loop is executed. + */ + void init(); + void shutdown(); + bool is_shutdown(); + bool is_running(); + + void set_status(enum swWorkerStatus _status) { + status = _status; + } + + void set_status_to_idle() { + set_status(SW_WORKER_IDLE); + } + + void set_status_to_busy() { + set_status(SW_WORKER_BUSY); + } + + void add_request_count() { + request_count++; + } + + bool is_busy() { + return status == SW_WORKER_BUSY; + } + + bool is_idle() { + return status == SW_WORKER_IDLE; + } }; struct StreamInfo { network::Socket *socket; network::Socket *last_connection; char *socket_file; + int socket_port; String *response_buffer; }; @@ -130,11 +211,18 @@ struct ProcessPool { bool reloading; bool running; 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; /** @@ -175,26 +263,31 @@ struct ProcessPool { uint8_t scheduler_warning; time_t warning_time; - int (*onTask)(ProcessPool *pool, swEventData *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); - int (*onWorkerNotFound)(ProcessPool *pool, pid_t pid, int status); sw_atomic_t round_id; 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++) { @@ -202,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++) { @@ -210,34 +303,79 @@ struct ProcessPool { } } - inline Worker *get_worker(int worker_id) { + Worker *get_worker(int worker_id) { return &(workers[worker_id - start_id]); } + Worker *get_worker_by_pid(pid_t pid) { + auto iter = map_->find(pid); + if (iter == map_->end()) { + return nullptr; + } + 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(); - int 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(swEventData *data, int *dst_worker_id); - 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_unix_socket(const char *socket_file, int blacklog); - int create_tcp_socket(const char *host, int port, int blacklog); + 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); - static int create(ProcessPool *pool, uint32_t worker_num, key_t msgqueue_key, int ipc_mode); + 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 -typedef swoole::ProcessPool swProcessPool; -typedef swoole::Worker swWorker; - static sw_inline int swoole_waitpid(pid_t __pid, int *__stat_loc, int __options) { int ret; do { @@ -247,10 +385,13 @@ static sw_inline int swoole_waitpid(pid_t __pid, int *__stat_loc, int __options) } static sw_inline int swoole_kill(pid_t __pid, int __sig) { - if (__pid <= 0) { - return -1; - } 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 fb0a34bd1c7..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,7 +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 { @@ -60,17 +86,4 @@ struct Socks5Proxy { buf[2] = method; } }; -} - -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, -}; - +} // namespace swoole diff --git a/include/swoole_reactor.h b/include/swoole_reactor.h index faedd6c5880..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 { @@ -86,6 +92,7 @@ class Reactor { PRIORITY_SIGNAL_CALLBACK, PRIORITY_TRY_EXIT, PRIORITY_MALLOC_TRIM, + PRIORITY_WORKER_CALLBACK, }; enum ExitCondition { @@ -97,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, }; @@ -110,7 +119,6 @@ class Reactor { */ int singal_no = 0; - uint32_t event_num = 0; uint32_t max_event_num = 0; bool running = false; @@ -118,10 +126,8 @@ class Reactor { bool once = false; bool wait_exit = false; bool destroyed = false; - /** - * callback signal - */ - bool check_signalfd = false; + bool bailout = false; + /** * reactor->wait timeout (millisecond) or -1 */ @@ -166,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); @@ -181,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); @@ -191,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); @@ -226,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); @@ -235,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); @@ -244,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); @@ -253,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]; @@ -276,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) { @@ -288,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() { @@ -324,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 35723d623ba..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" @@ -39,40 +43,10 @@ #include #include #include +#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, -}; - -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 { @@ -81,6 +55,11 @@ namespace http_server { struct Request; } +class Server; +struct Manager; + +typedef std::function WorkerFn; + struct Session { SessionId id; int fd; @@ -90,67 +69,40 @@ struct Session { struct Connection { /** - * file descript + * It must be in the header. When set to 0, it means that connection does not exist. + * One-write and multiple-read operation is thread-safe + * system fd must be 0. en: signalfd, listen socket */ + uint8_t active; + SocketType socket_type; int fd; - /** - * session id - */ + int worker_id; SessionId session_id; - /** - * socket type, SW_SOCK_TCP or SW_SOCK_UDP - */ - enum swSocket_type socket_type; //-------------------------------------------------------------- - /** - * is active - * system fd must be 0. en: signalfd, listen socket - */ - uint8_t active; #ifdef SW_USE_OPENSSL uint8_t ssl; uint8_t ssl_ready; #endif - //-------------------------------------------------------------- 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 - //-------------------------------------------------------------- - /** - * server is actively close the connection - */ + // If it is equal to 1, it means server actively closed the connection uint8_t close_actively; uint8_t closed; uint8_t close_queued; uint8_t closing; uint8_t close_reset; uint8_t peer_closed; - /** - * protected connection, do not close connection when receiving/sending timeout - */ + // protected connection, do not close connection when receiving/sending timeout uint8_t protect; - //-------------------------------------------------------------- uint8_t close_notify; uint8_t close_force; - //-------------------------------------------------------------- - /** - * ReactorThread id - */ - uint16_t reactor_id; - /** - * close error code - */ + ReactorId reactor_id; uint16_t close_errno; - /** - * from which socket fd - */ int server_fd; sw_atomic_t recv_queued_bytes; uint32_t send_queued_bytes; @@ -165,7 +117,7 @@ struct Connection { */ void *object; /** - * socket info + * socket, only operated in the main process */ network::Socket *socket; /** @@ -190,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; -}; - -struct SendData { - DataHead info; - const char *data; + 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 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 { @@ -267,20 +198,21 @@ 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; - int socket_fd = 0; network::Socket *socket = nullptr; pthread_t thread_id = 0; + uint16_t heartbeat_idle_time = 0; + /** * check data eof */ @@ -354,51 +286,80 @@ 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); bool ssl_add_sni_cert(const std::string &name, SSLContext *context); bool ssl_init(); + + 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() const { + return port; + } + const char *get_host() const { + return host.c_str(); + } + SocketType get_type() const { + return type; + } + int get_fd() const { + return socket ? socket->fd : -1; + } + + size_t get_connection_num() const; }; struct ServerGS { @@ -412,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_; @@ -437,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; @@ -448,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; @@ -456,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(); @@ -474,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, @@ -489,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, @@ -501,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 { @@ -528,6 +582,7 @@ class Server { HOOK_MANAGER_START, HOOK_MANAGER_TIMER, HOOK_PROCESS_TIMER, + HOOK_END = SW_MAX_HOOK_TYPE - 1, }; enum CloseFlag { @@ -549,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. @@ -558,7 +613,6 @@ class Server { int worker_uid = 0; int worker_groupid = 0; - void **worker_input_buffers = nullptr; /** * worker process max request @@ -572,6 +626,7 @@ class Server { int null_fd = -1; uint32_t max_wait_time = SW_WORKER_MAX_WAIT_TIME; + uint32_t worker_max_concurrency = UINT_MAX; /*----------------------------Reactor schedule--------------------------------*/ sw_atomic_t worker_round_id = 0; @@ -683,12 +738,12 @@ 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_idle_time = 0; uint16_t heartbeat_check_interval = 0; - uint32_t heartbeat_check_lasttime = 0; time_t reload_time = 0; time_t warning_time = 0; @@ -697,7 +752,7 @@ class Server { TimerNode *heartbeat_timer = nullptr; /* buffer output/input setting*/ - uint32_t output_buffer_size = SW_OUTPUT_BUFFER_SIZE; + uint32_t output_buffer_size = UINT_MAX; uint32_t input_buffer_size = SW_INPUT_BUFFER_SIZE; uint32_t max_queued_bytes = 0; @@ -711,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; @@ -730,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; @@ -746,65 +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; -#endif + uint32_t compression_min_length; /** * 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,15 +953,9 @@ class Server { std::function onTask; std::function onFinish; /** - * Chunk control + * for MessageBus */ - void **(*create_buffers)(Server *serv, uint32_t buffer_num) = nullptr; - void (*free_buffers)(Server *serv, uint32_t buffer_num, void **buffers) = nullptr; - void *(*get_buffer)(Server *serv, DataHead *info) = nullptr; - size_t (*get_buffer_len)(Server *serv, DataHead *info) = nullptr; - void (*add_buffer_len)(Server *serv, DataHead *info, size_t len) = nullptr; - void (*move_buffer)(Server *serv, PipeBuffer *buffer) = nullptr; - size_t (*get_packet)(Server *serv, EventData *req, char **data_ptr) = nullptr; + std::function msg_id_generator; /** * Hook */ @@ -872,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; } @@ -889,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; } @@ -940,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()) { @@ -984,15 +1116,20 @@ class Server { } } - inline bool is_hash_dispatch_mode() { - return dispatch_mode == SW_DISPATCH_FDMOD || dispatch_mode == SW_DISPATCH_IPMOD; + bool is_master_thread() { + return swoole_get_thread_type() == Server::THREAD_MASTER; + } + + bool is_hash_dispatch_mode() { + return dispatch_mode == DISPATCH_FDMOD || dispatch_mode == DISPATCH_IPMOD || + dispatch_mode == DISPATCH_CO_CONN_LB; } - inline bool is_support_send_yield() { + bool is_support_send_yield() { return is_hash_dispatch_mode(); } - inline bool if_require_packet_callback(ListenPort *port, bool isset) { + bool if_require_packet_callback(ListenPort *port, bool isset) { #ifdef SW_USE_OPENSSL return (port->is_dgram() && !port->ssl && !isset); #else @@ -1000,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 @@ -1008,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]); @@ -1021,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]); } @@ -1029,60 +1170,108 @@ class Server { return nullptr; } + 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(); } - inline String *get_worker_input_buffer(int reactor_id) { - if (is_base_mode()) { - return (String *) worker_input_buffers[0]; - } else { - return (String *) worker_input_buffers[reactor_id]; - } + 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; } - inline bool is_shutdown() { + 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() { + if (is_manager()) { + return true; + } + if (is_task_worker() && !task_enable_coroutine) { + return true; + } + return false; + } + bool is_shutdown() { return gs->shutdown; } - inline bool is_valid_connection(Connection *conn) { + // can only be used in the main process + bool is_valid_connection(Connection *conn) { return (conn && conn->socket && conn->active && conn->socket->fd_type == SW_FD_SESSION); } + bool is_healthy_connection(double now, Connection *conn); + static int is_dgram_event(uint8_t type) { switch (type) { case SW_SERVER_EVENT_RECV_DGRAM: @@ -1095,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: @@ -1107,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); @@ -1124,41 +1315,44 @@ 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) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SSL_NOT_READY, "SSL not ready"); return nullptr; } #endif 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_by_session_id(SessionId session_id) { - return get_connection(get_connection_fd(session_id)); - } - - inline Session *get_session(SessionId session_id) { - return &session_list[session_id % SW_SESSION_LIST_SIZE]; + Connection *get_connection_for_iterator(int fd) { + Connection *conn = get_connection(fd); + if (conn && conn->active && !conn->closed) { +#ifdef SW_USE_OPENSSL + if (conn->ssl && !conn->ssl_ready) { + return nullptr; + } +#endif + return conn; + } + return nullptr; } - inline void lock() { - lock_.lock(); + Connection *get_connection_by_session_id(SessionId session_id) const { + return get_connection(get_connection_fd(session_id)); } - inline void unlock() { - lock_.unlock(); + Session *get_session(SessionId session_id) { + return &session_list[session_id % SW_SESSION_LIST_SIZE]; } - void close_port(bool only_stream_port); void clear_timer(); static void timer_callback(Timer *timer, TimerNode *tnode); @@ -1168,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); @@ -1193,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; @@ -1215,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(); @@ -1222,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, pid_t pid, int status); + 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, pid_t pid, int 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 */ @@ -1265,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 eae13a29dbd..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(); -int 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 6e1dcd23a34..35606ee3811 100644 --- a/include/swoole_socket.h +++ b/include/swoole_socket.h @@ -10,19 +10,20 @@ | to obtain it through the world-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 | +----------------------------------------------------------------------+ */ #pragma once -#include "swoole.h" - #include #include #include #include +#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) +#include +#endif #include #include #include @@ -31,6 +32,7 @@ #include #include +#include "swoole.h" #include "swoole_ssl.h" #include "swoole_buffer.h" #include "swoole_file.h" @@ -39,29 +41,32 @@ #define SOCK_NONBLOCK O_NONBLOCK #endif +#ifdef __sun +#define s6_addr8 _S6_un._S6_u8 +#define s6_addr16 _S6_un._S6_u16 +#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; @@ -89,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; @@ -118,19 +135,27 @@ struct IOVector { void update_iterator(ssize_t __n); - inline struct iovec *get_iterator() { + struct iovec *get_iterator() { return iov_iterator; } - inline int get_remain_count() { + size_t length() { + size_t len = 0; + SW_LOOP_N(count) { + len += iov[i].iov_len; + } + return len; + } + + 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; } }; @@ -143,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; @@ -160,6 +185,7 @@ struct Socket { uchar ssl_renegotiation : 1; uchar ssl_handshake_buffer_set : 1; uchar ssl_quiet_shutdown : 1; + uchar ssl_closed_ : 1; #ifdef SW_SUPPORT_DTLS uchar dtls : 1; #endif @@ -172,10 +198,9 @@ struct Socket { uchar skip_recv : 1; uchar recv_wait : 1; uchar event_hup : 1; + uchar dont_restart : 1; - /** - * memory buffer size; - */ + // memory buffer size [user space] uint32_t buffer_size; uint32_t chunk_size; @@ -203,14 +228,14 @@ struct Socket { size_t total_recv_bytes; size_t total_send_bytes; - /** - * for reactor - */ + // for reactor int handle_send(); int handle_sendfile(); - /** - * socket option - */ + // user space memory buffer + void set_memory_buffer_size(uint32_t _buffer_size) { + buffer_size = _buffer_size; + } + // socket option [kernel buffer] bool set_buffer_size(uint32_t _buffer_size); bool set_recv_buffer_size(uint32_t _buffer_size); bool set_send_buffer_size(uint32_t _buffer_size); @@ -218,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; @@ -291,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)); } /** @@ -303,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); @@ -326,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); @@ -348,69 +380,81 @@ 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); + 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); bool ssl_get_peer_certificate(String *buf); bool ssl_verify(bool allow_self_signed); bool ssl_check_host(const char *tls_host_name); void ssl_catch_error(); + bool ssl_shutdown(); void ssl_close(); + 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; } + bool isset_readable_event() { + return events & SW_EVENT_READ; + } + + bool isset_writable_event() { + return events & SW_EVENT_WRITE; + } + 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); } @@ -446,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); } @@ -453,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) { + 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; @@ -461,20 +517,20 @@ struct Socket { return sendto(addr, data, len, flags); } - inline ssize_t sendto(const Address &dst_addr, const void *data, size_t len, int flags) { + 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) { + int catch_error(int err) const { switch (err) { case EFAULT: abort(); return SW_ERROR; case EBADF: + case ENOENT: + return SW_INVALID; case ECONNRESET: -#ifdef __CYGWIN__ case ECONNABORTED: -#endif case EPIPE: case ENOTCONN: case ETIMEDOUT: @@ -487,6 +543,9 @@ struct Socket { case SW_ERROR_SSL_RESET: return SW_CLOSE; case EAGAIN: +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif #ifdef HAVE_KQUEUE case ENOBUFS: #endif @@ -497,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); @@ -522,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; @@ -560,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 20436f94ebf..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,23 +33,37 @@ #include #include #include +#include #if OPENSSL_VERSION_NUMBER >= 0x10100000L #define SW_SUPPORT_DTLS #endif -enum swSSL_create_flag { +#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3000000fL +#undef SW_SUPPORT_DTLS +#endif + +#ifdef OPENSSL_IS_BORINGSSL +#define BIO_CTRL_DGRAM_SET_CONNECTED 32 +#define BIO_CTRL_DGRAM_SET_PEER 44 +#define BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT 45 +#define BIO_dgram_get_peer(b,peer) \ + (int)BIO_ctrl(b, BIO_CTRL_DGRAM_GET_PEER, 0, (char *)(peer)) +#define OPENSSL_assert(x) assert(x) +#endif + +enum swSSLCreateFlag { SW_SSL_SERVER = 1, SW_SSL_CLIENT = 2, }; -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, @@ -61,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, @@ -108,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; @@ -122,13 +141,17 @@ struct SSLContext { return context; } + bool ready() { + return context != nullptr; + } + void set_protocols(uint32_t _protocols) { protocols = _protocols; } 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; @@ -137,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; @@ -154,10 +177,13 @@ struct SSLContext { }; } -void swSSL_init(void); -void swSSL_init_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 df984f4b1df..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 | +----------------------------------------------------------------------+ */ @@ -67,12 +67,48 @@ struct TableRow { } void set_value(TableColumn *col, void *value, size_t vlen); + void get_value(TableColumn *col, double *dval); + void get_value(TableColumn *col, long *lval); + void get_value(TableColumn *col, char **strval, TableStringLength *strlen); }; struct TableIterator { - uint32_t absolute_index; - uint32_t collision_index; - TableRow *row; + size_t row_memory_size_; + uint32_t absolute_index = 0; + uint32_t collision_index = 0; + TableRow *current_; + Mutex *mutex_; + + explicit TableIterator(size_t row_size) { + current_ = (TableRow *) sw_malloc(row_size); + if (!current_) { + throw std::bad_alloc(); + } + mutex_ = new Mutex(Mutex::PROCESS_SHARED); + row_memory_size_ = row_size; + reset(); + } + + void lock() { + mutex_->lock(); + } + + void unlock() { + mutex_->unlock(); + } + + void reset() { + absolute_index = 0; + collision_index = 0; + sw_memset_zero(current_, row_memory_size_); + } + + ~TableIterator() { + if (current_) { + sw_free(current_); + } + delete mutex_; + } }; enum TableFlag { @@ -117,9 +153,6 @@ struct TableColumn { class Table { private: - Table() = delete; - ~Table() = delete; - std::unordered_map *column_map; Mutex *mutex; size_t size; @@ -142,26 +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(); + // release shared memory void destroy(); - bool is_created() { + bool is_created() const { return created; } @@ -173,7 +213,7 @@ class Table { hash_func = _fn; } - size_t get_size() { + size_t get_size() const { return size; } @@ -191,18 +231,39 @@ class Table { } } - size_t count() { + size_t count() const { return row_num; } + bool exists(const char *key, uint16_t keylen) { + TableRow *_rowlock = nullptr; + const TableRow *row = get(key, keylen, &_rowlock); + _rowlock->unlock(); + return row != nullptr; + } + + bool exists(const std::string &key) { + return exists(key.c_str(), key.length()); + } + TableRow *current() { - return iterator->row; + return iterator->current_; } void rewind() { - sw_memset_zero(iterator, sizeof(*iterator)); + iterator->lock(); + iterator->reset(); + 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; @@ -210,22 +271,41 @@ class Table { return rows[index]; } - void check_key_length(uint16_t *keylen) { + TableRow *alloc_row() { + lock(); + auto new_row = (TableRow *) pool->alloc(0); + unlock(); + return new_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 6d8112a52a2..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,6 +65,35 @@ 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: + std::stack list_; + + public: + void add(Callback fn) { + list_.push(fn); + } + + ~DeferTask() { + while (!list_.empty()) { + auto fn = list_.top(); + fn(nullptr); + list_.pop(); + } + } +}; + template class ScopeGuard { public: @@ -92,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 {}; @@ -99,7 +179,7 @@ template inline ScopeGuard operator+(ScopeGuardOnExit, Fun &&fn) { return ScopeGuard(std::forward(fn)); } -} +} // namespace detail // Helper macro #define ON_SCOPE_EXIT \ @@ -107,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])) { @@ -138,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 0aad7eb9891..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 6 +#define SWOOLE_MAJOR_VERSION 6 +#define SWOOLE_MINOR_VERSION 0 #define SWOOLE_RELEASE_VERSION 0 -#define SWOOLE_EXTRA_VERSION "alpha" -#define SWOOLE_VERSION "4.6.0-alpha" -#define SWOOLE_VERSION_ID 40600 -#define SWOOLE_API_VERSION_ID 0x202012a +#define SWOOLE_EXTRA_VERSION "" +#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 1383cbd5afd..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 | +----------------------------------------------------------------------+ */ @@ -28,35 +28,35 @@ #define SW_WEBSOCKET_EXT64_LENGTH 0x7F #define SW_WEBSOCKET_CLOSE_CODE_LEN 2 #define SW_WEBSOCKET_CLOSE_REASON_MAX_LEN 125 -#define SW_WEBSOCKET_OPCODE_MAX WEBSOCKET_OPCODE_PONG - -enum swWebsocket_status { - WEBSOCKET_STATUS_NONE = 0, - WEBSOCKET_STATUS_CONNECTION = 1, - WEBSOCKET_STATUS_HANDSHAKE = 2, - WEBSOCKET_STATUS_ACTIVE = 3, - WEBSOCKET_STATUS_CLOSING = 4, +#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 { + +enum Status { + STATUS_NONE = 0, + STATUS_CONNECTION = 1, + STATUS_HANDSHAKE = 2, + STATUS_ACTIVE = 3, + STATUS_CLOSING = 4, }; -enum swWebSocket_frame_flag { - SW_WEBSOCKET_FLAG_FIN = 1 << 0, /* BC: must be 1 */ - SW_WEBSOCKET_FLAG_COMPRESS = 1 << 1, +enum Flag { + FLAG_FIN = 1 << 0, /* BC: must be 1 */ + FLAG_COMPRESS = 1 << 1, // readonly for user - SW_WEBSOCKET_FLAG_RSV1 = 1 << 2, - SW_WEBSOCKET_FLAG_RSV2 = 1 << 3, - SW_WEBSOCKET_FLAG_RSV3 = 1 << 4, - SW_WEBSOCKET_FLAG_MASK = 1 << 5, + FLAG_RSV1 = 1 << 2, + FLAG_RSV2 = 1 << 3, + FLAG_RSV3 = 1 << 4, + FLAG_MASK = 1 << 5, // for encoder/decoder - SW_WEBSOCKET_FLAG_ENCODE_HEADER_ONLY = 1 << 6, + FLAG_ENCODE_HEADER_ONLY = 1 << 6, + FLAGS_ALL = /* used to prevent overflow */ + FLAG_FIN | FLAG_RSV1 | FLAG_RSV2 | FLAG_RSV3 | FLAG_MASK | FLAG_COMPRESS }; -enum swWebSocket_frame_union_flag { - SW_WEBSOCKET_FLAGS_ALL = /* used to prevent overflow */ - SW_WEBSOCKET_FLAG_FIN | SW_WEBSOCKET_FLAG_RSV1 | SW_WEBSOCKET_FLAG_RSV2 | SW_WEBSOCKET_FLAG_RSV3 | - SW_WEBSOCKET_FLAG_MASK | SW_WEBSOCKET_FLAG_COMPRESS -}; - -struct swWebSocket_frame_header { +struct Header { /** * fin:1 rsv1:1 rsv2:1 rsv3:1 opcode:4 */ @@ -69,8 +69,8 @@ struct swWebSocket_frame_header { uchar MASK : 1; }; -struct swWebSocket_frame { - swWebSocket_frame_header header; +struct Frame { + Header header; char mask_key[SW_WEBSOCKET_MASK_LEN]; uint16_t header_length; size_t payload_length; @@ -79,78 +79,84 @@ struct swWebSocket_frame { #define WEBSOCKET_VERSION 13 -enum swWebsocket_opcode { - WEBSOCKET_OPCODE_CONTINUATION = 0x0, - WEBSOCKET_OPCODE_TEXT = 0x1, - WEBSOCKET_OPCODE_BINARY = 0x2, - WEBSOCKET_OPCODE_CLOSE = 0x8, - WEBSOCKET_OPCODE_PING = 0x9, - WEBSOCKET_OPCODE_PONG = 0xa, +enum Opcode { + OPCODE_CONTINUATION = 0x0, + OPCODE_TEXT = 0x1, + OPCODE_BINARY = 0x2, + OPCODE_CLOSE = 0x8, + OPCODE_PING = 0x9, + OPCODE_PONG = 0xa, }; -enum swWebsocket_close_reason { - WEBSOCKET_CLOSE_NORMAL = 1000, - WEBSOCKET_CLOSE_GOING_AWAY = 1001, - WEBSOCKET_CLOSE_PROTOCOL_ERROR = 1002, - WEBSOCKET_CLOSE_DATA_ERROR = 1003, - WEBSOCKET_CLOSE_STATUS_ERROR = 1005, - WEBSOCKET_CLOSE_ABNORMAL = 1006, - WEBSOCKET_CLOSE_MESSAGE_ERROR = 1007, - WEBSOCKET_CLOSE_POLICY_ERROR = 1008, - WEBSOCKET_CLOSE_MESSAGE_TOO_BIG = 1009, - WEBSOCKET_CLOSE_EXTENSION_MISSING = 1010, - WEBSOCKET_CLOSE_SERVER_ERROR = 1011, - WEBSOCKET_CLOSE_TLS = 1015, +enum CloseReason { + CLOSE_NORMAL = 1000, + CLOSE_GOING_AWAY = 1001, + CLOSE_PROTOCOL_ERROR = 1002, + CLOSE_DATA_ERROR = 1003, + CLOSE_STATUS_ERROR = 1005, + CLOSE_ABNORMAL = 1006, + CLOSE_MESSAGE_ERROR = 1007, + CLOSE_POLICY_ERROR = 1008, + 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, }; -static inline uchar swWebSocket_get_flags(swWebSocket_frame *frame) { +static inline uchar get_flags(Frame *frame) { uchar flags = 0; if (frame->header.FIN) { - flags |= SW_WEBSOCKET_FLAG_FIN; + flags |= FLAG_FIN; } if (frame->header.RSV1) { - flags |= SW_WEBSOCKET_FLAG_RSV1; + flags |= FLAG_RSV1; } if (frame->header.RSV2) { - flags |= SW_WEBSOCKET_FLAG_RSV2; + flags |= FLAG_RSV2; } if (frame->header.RSV3) { - flags |= SW_WEBSOCKET_FLAG_RSV3; + flags |= FLAG_RSV3; } if (frame->header.MASK) { - flags |= SW_WEBSOCKET_FLAG_MASK; + flags |= FLAG_MASK; } return flags; } -static inline uchar swWebSocket_set_flags(uchar fin, uchar mask, uchar rsv1, uchar rsv2, uchar rsv3) { +static inline uchar set_flags(uchar fin, uchar mask, uchar rsv1, uchar rsv2, uchar rsv3) { uchar flags = 0; if (fin) { - flags |= SW_WEBSOCKET_FLAG_FIN; + flags |= FLAG_FIN; } if (mask) { - flags |= SW_WEBSOCKET_FLAG_MASK; + flags |= FLAG_MASK; } if (rsv1) { - flags |= SW_WEBSOCKET_FLAG_RSV1; + flags |= FLAG_RSV1; } if (rsv2) { - flags |= SW_WEBSOCKET_FLAG_RSV2; + flags |= FLAG_RSV2; } if (rsv3) { - flags |= SW_WEBSOCKET_FLAG_RSV3; + flags |= FLAG_RSV3; } return flags; } -bool swWebSocket_encode(swString *buffer, const char *data, size_t length, char opcode, uint8_t flags); -bool swWebSocket_decode(swWebSocket_frame *frame, char *data, size_t length); -int swWebSocket_pack_close_frame(swString *buffer, int code, char *reason, size_t length, uint8_t flags); -void swWebSocket_print_frame(swWebSocket_frame *frame); +bool encode(String *buffer, const char *data, size_t length, char opcode, uint8_t flags); +bool decode(Frame *frame, char *data, size_t length); +int pack_close_frame(String *buffer, int code, char *reason, size_t length, uint8_t flags); +void print_frame(Frame *frame); -inline bool swWebSocket_decode(swWebSocket_frame *frame, swString *str) { - return swWebSocket_decode(frame, str->str, str->length); +static inline bool decode(Frame *frame, String *str) { + return decode(frame, str->str, str->length); } -ssize_t swWebSocket_get_package_length(swProtocol *protocol, swSocket *conn, const char *data, uint32_t length); -int swWebSocket_dispatch_frame(swProtocol *protocol, swSocket *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 83609413402..00000000000 --- a/make.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/sh -e -__DIR__=$(cd "$(dirname "$0")";pwd) -__EXT_DIR__=$(php-config --extension-dir) - -cd "${__DIR__}" - -if [ "$1" = "cmake" ] ;then - phpize - ./configure --enable-openssl --enable-swoole-json --enable-sockets --enable-mysqlnd --enable-http2 - cmake . - make -j 8 - exit 0 -fi - -if [ "$1" = "clean" ] ;then - make clean - phpize --clean - exit 0 -fi - -if [ "$1" = "install-module" ] ;then - make ext-swoole - cp lib/swoole.so "${__EXT_DIR__}" - echo "cp lib/swoole.so ${__EXT_DIR__}" - exit 0 -fi - -if [ "$1" = "sync-module" ] ;then - # thirtparty - git submodule init - git submodule update - 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 sync-module" - echo "./make.sh clean" - echo "./make.sh debug" - echo "./make.sh" - echo "./make.sh library" - exit 0 -fi - -phpize -if [ "$1" = "debug" ] ;then - ./configure --enable-openssl --enable-swoole-json --enable-swoole-curl --enable-sockets --enable-mysqlnd --enable-http2 --enable-debug-log -else - ./configure --enable-openssl --enable-swoole-json --enable-swoole-curl --enable-sockets --enable-mysqlnd --enable-http2 -fi -make clean -make -j 8 -make install 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 201a901bd37..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,26 +22,38 @@ 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 + no + + + Lu Fei + lufei + lufei@php.net + yes + + + Bruce Dou + doubaokun + doubaokun@php.net yes - 2020-11-20 + 2024-12-16 - 4.5.8 - 4.0 + 6.0.0 + 6.0 stable @@ -54,54 +61,128 @@ Apache2.0 - New APIs - --- - + Added swoole_error_log function (swoole/swoole-src@67d2bff) (@matyhtf) - + Supported SSL for readVector and writeVector (#3857) (@huanghantao) - - Enhancement - --- - + Did not wait when the child process does not exist (#3832) (@matyhtf) - + Supported DTLS 16k record (#3849) (@matyhtf) - + Supported priority for cookie (#3854) (@matyhtf) - + Supported more CURL options (swoole/library#71) (@sy-records) - + Handled CURL header names in case insensitive manner (swoole/library#76) (@filakhtov) (@twose) (@sy-records) - - Fixed - --- - * Fixed readv_all and writev_all when trigger EAGAIN errno (#3830) (@huanghantao) - * Fixed warning for PHP8 (swoole/swoole-src@03f3fb0) (@matyhtf) - * Fixed binary security of swoole table keys (#3842) (@twose) - * Fixed System::writeFile append file issue when using FILE_APPEND (swoole/swoole-src@a71956d) (@matyhtf) - * Fixed CURLOPT_WRITEFUNCTION (swoole/library#74) (@sy-records) - * Fixed memory overread when parse form-data boundary (#3858) (@twose) - * Fixed is_callable() can not access internal private callable on PHP8 (#3859) (@twose) - - Kernel - --- - * Refactored memory allocation function, replace macro with SwooleG.std_allocator (#3853) (@matyhtf) - * Refactored pipe (#3841) (@matyhtf) + - Added multi-threading support, require the ZTS version of PHP. Add `--enable-swoole-thread` option to the configure command to activate it. + - Added a new thread class `Swoole\Thread`. @matyhtf + - Introduced thread lock `Swoole\Thread\Lock`. @matyhtf + - Added thread atomic counter `Swoole\Thread\Atomic`, `Swoole\Thread\Atomic\Long`. @matyhtf + - Added safe concurrent containers `Swoole\Thread\Map`, `Swoole\Thread\ArrayList`, `Swoole\Thread\Queue`. @matyhtf + - The file asynchronous operation supports using `io_uring` as the underlying engine for file asynchronous operations. When liburing is installed and Swoole is compiled with the --enable-iouring option, the asynchronous operations of functions such as file_get_contents, file_put_contents, fopen, fclose, fread, fwrite, mkdir, unlink, fsync, fdatasync, rename, fstat, lstat, and filesize will be implemented by io_uring. @matyhtf @NathanFreeman + - Upgraded `Boost Context` to version 1.84. Now, Loongson CPUs can also support coroutines. @NathanFreeman + - Added `Swoole\Thread\Map::find()` method. @matyhtf + - Added `Swoole\Thread\ArrayList::find()` method. @matyhtf + - Added `Swoole\Thread\ArrayList::offsetUnset()` method. @matyhtf + - Added `Swoole\Process::getAffinity()` method. @matyhtf + - Added `Swoole\Thread::setName()` method. @matyhtf + - Added `Swoole\Thread::setAffinity()` method. @matyhtf + - Added `Swoole\Thread::getAffinity()` method. @matyhtf + - Added `Swoole\Thread::setPriority()` method. @matyhtf + - Added `Swoole\Thread::getPriority()` method. @matyhtf + - Added `Swoole\Thread::gettid()` method. + - The file asynchronous engine `iouring` supports multi-threaded polling mode `IORING_SETUP_SQPOLL`. @NathanFreeman + - Added `iouring_workers` to modify the number of `iouring` threads. @NathanFreeman + - Added `iouring_flags` to support modifying the `iouring` working mode. @NathanFreeman + - Added `Swoole\Thread\Barrier` for multi-thread synchronization barrier. @matyhtf + - Added new function and class to set cookies. @matyhtf @NathanFreeman + - Added `non-blocking, reentrant coroutine mutex lock`, which can be used between processes/threads without blocking them. @NathanFreeman + - `Swoole\Coroutine\Socket::getOption()` supports the `TCP_INFO` option. @matyhtf + - `Swoole\Client` synchronous blocking client supports `http` proxy. @matyhtf + - Added asynchronous non-blocking `TCP/UDP/Unix socket` client `Swoole\Async\Client`. @matyhtf + - Optimized the `Swoole\Redis\Server::format()` method to support zero-copy memory, support `redis` nested structure. @matyhtf + - Supports the high-performance compression tool `Zstd`. You only need to add `--enable-zstd` when compiling `Swoole`, and then `zstd` can be used to compress or decode responses between the `http` client and server. @NathanFreeman + - Fixed the issue where installation via `pecl` was not possible. @remicollet + - Fixed the bug where setting `keepalive` was not possible for `Swoole\Coroutine\FastCGI\Client`. @NathanFreeman + - Fixed the issue where exceeding the `max_input_vars` would throw an error, causing the process to restart repeatedly. @NathanFreeman + - Fixed unknown issues caused by using `Swoole\Event::wait()` within a coroutine. @matyhtf + - Fixed the problem where `proc_open` does not support pty in coroutine mode. @matyhtf + - Fixed segmentation fault issues with `pdo_sqlite` on PHP 8.3. @NathanFreeman + - Fixed unnecessary warnings during the compilation of `Swoole`. @Appla @NathanFreeward + - Fixed the error thrown by zend_fetch_resource2_ex when `STDOUT/STDERR` are already closed. @Appla @matyhtf + - Fixed ineffective `set_tcp_nodelay` configuration. @matyhtf + - Fixed the occasional unreachable branch issue during file upload. @NathanFreeman + - Fixed the problem where setting `dispatch_func` would cause PHP's internals to throw errors. @NathanFreeman + - Fixed the deprecation of AC_PROG_CC_C99 in autoconf >= 2.70. @petk + - Capture exceptions when thread creation fails. @matyhtf + - Fixed the undefined problem with `_tsrm_ls_cache`. @jingjingxyk + - Fixed the fatal compile error with `GCC 14`. @remicollet + - Fixed the dynamic property issue in `Swoole\Http2\Request`. @guandeng + - Fixed the occasional resource unavailability issue in the `pgsql` coroutine client. @NathanFreeman + - Fixed the issue of 503 errors due to not resetting related parameters during process restart. @matyhtf + - Fixed the inconsistency between `$request->server['request_method']` and `$request->getMethod()` when `HTTP2` is enabled. @matyhtf + - Fixed incorrect `content-type` when uploading files. @matyhtf + - Fixed code errors in the `http2` coroutine client. @matyhtf + - Fixed the missing `worker_id` property in `Swoole\Server`. @cjavad + - Fixed errors related to `brotli` in `config.m4`. @fundawang + - Fixed the invalid `Swoole\Http\Response::create` under multi-threading. @matyhtf + - Fixed compilation errors in the `macos` environment. @matyhtf + - Fixed the issue of threads not being able to exit safely. @matyhtf + - Fixed the issue where the static variable for response time returned by `Swoole\Http\Response` in multi-threaded mode was not generated separately for each thread. @matyhtf @NathanFreeman + - Fixed `Fatal error` issue caused by `PHP-8.4`'s `timeout` feature in ZTS mode. @matyhtf + - Fixed compatibility issue with the `exit()` `hook` function for `PHP-8.4`. @remicollet + - Fixed the issue where `Swoole\Thread::getNativeId()` did not work in `cygwin`. @matyhtf + - Fixed the issue causing `SIGSEGV` in `Swoole\Coroutine::getaddrinfo()` method. @matyhtf + - Fixed the issue where the runtime TCP module did not support dynamically enabling SSL encryption. @matyhtf + - Fixed the issue where the HTTP client had an incorrect timeout after running for a long time. @matyhtf + - Fixed the problem where the mutex lock of `Swoole\Table` could not be used before the process exited. @matyhtf + - Fixed the failure of `Swoole\Server::stop()` when using named parameters. @matyhtf + - Fixed the crash caused by `Swoole\Thread\Map::toArray()` not copying the key. @matyhtf + - Fixed the issue of being unable to delete nested numeric keys in `Swoole\Thread\Map`. @matyhtf + - Removed unnecessary checks for `socket structs`. @petk + - Upgraded Swoole Library. @deminy + - Added support for status code 451 in `Swoole\Http\Response`. @abnegate + - Synchronized `file` operation code across different PHP versions. @NathanFreeman + - Synchronized `pdo` operation code across different PHP versions. @NathanFreeman + - Optimized the code for `Socket::ssl_recv()`. @matyhtf + - Improved config.m4; some configurations can now set library locations via `pkg-config`. @NathanFreeman + - Optimized the use of dynamic arrays during `request header parsing`. @NathanFreeman + - Optimized file descriptor `fd` lifecycle issues in multi-threading mode. @matyhtf + - Optimized some fundamental coroutine logic. @matyhtf + - Upgraded the Oracle database version for CI testing. @gvenzl + - Optimized the underlying logic of `sendfile`. @matyhtf + - Replaced `PHP_DEF_HAVE` with `AC_DEFINE_UNQUOTED` in `config.m4`. @petk + - Optimized the logic related to `heartbeat`, `shutdown`, and `stop` for the server in multi-threaded mode. @matyhtf + - Optimized to avoid linking `librt` when `glibc` version is greater than 2.17. @matyhtf + - Enhanced the HTTP client to accept duplicate request headers. @matyhtf + - Optimized `Swoole\Http\Response::write()`. @matyhtf + - `Swoole\Http\Response::write()` can now send HTTP/2 protocol. @matyhtf + - Compatible with `PHP 8.4`. @matyhtf @NathanFreeman + - Added the ability for asynchronous writing at the underlying socket level. @matyhtf + - Optimized `Swoole\Http\Response`. @NathanFreeman + - Improved underlying error messages. @matyhtf + - Supported sharing PHP native sockets in multi-threaded mode. @matyhtf + - Optimized static file service and fixed static file path error issues. @matyhtf + - Multi-thread mode `SWOOLE_THREAD` supports restarting worker threads. @matyhtf + - Multi-thread mode `SWOOLE_THREAD` supports starting timers in the `Manager` thread. @matyhtf + - Compatible with the `curl` extension of `PHP-8.4`. @matyhtf @NathanFreeman + - Rewrite the underlying `Swoole` code using `iouring`. @matyhtf @NathanFreeman + - Optimized timers so that synchronous processes do not depend on signals. @matyhtf + - Optimized the `Swoole\Coroutine\System::waitSignal()` method to allow listening to multiple signals simultaneously. @matyhtf + - No longer supports `PHP 8.0`. + - No longer supports `Swoole\Coroutine\MySQL` coroutine client. + - No longer supports `Swoole\Coroutine\Redis` coroutine client. + - No longer supports `Swoole\Coroutine\PostgreSQL` coroutine client. + - Removed `Swoole\Coroutine\System::fread()`, `Swoole\Coroutine\System::fwrite()`, and `Swoole\Coroutine\System::fgets()` methods. - - - - - - - - + + + + + + + + + @@ -111,8 +192,7 @@ - - + @@ -124,6 +204,7 @@ + @@ -135,6 +216,7 @@ + @@ -145,33 +227,46 @@ + + + + + + + + + + + + + + - - + + + + - - - @@ -215,12 +310,14 @@ + + @@ -231,6 +328,7 @@ + @@ -286,10 +384,11 @@ - + + + - @@ -298,54 +397,43 @@ - - - - + - - - + + - + - - - + + - - - - - - + - @@ -371,52 +459,56 @@ + - - - - - + + + + + - - - - - + - + + + + + + + + @@ -424,12 +516,9 @@ - - - @@ -455,16 +544,16 @@ + - - + @@ -475,22 +564,35 @@ - + + + + + + + + + + + + + + + + + + + - - - - - @@ -504,50 +606,141 @@ - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - + + + + + + + + - @@ -574,10 +767,12 @@ + + @@ -595,15 +790,37 @@ + - - - + + + + + + + + + + + + + + + + + + + + + + + + @@ -616,9 +833,12 @@ + + + @@ -645,10 +865,10 @@ - + @@ -669,13 +889,19 @@ + + + + + + @@ -688,24 +914,12 @@ + - - - - - - - - - - - - - @@ -730,11 +944,17 @@ + + + + + + @@ -750,13 +970,38 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -793,6 +1038,7 @@ + @@ -808,18 +1054,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -832,17 +1104,21 @@ + + + + @@ -854,8 +1130,12 @@ + + + + @@ -873,13 +1153,23 @@ + + + + + + + + + + @@ -896,6 +1186,16 @@ + + + + + + + + + + @@ -907,21 +1207,37 @@ + + + + + + + + + + + + + + + + @@ -931,6 +1247,15 @@ + + + + + + + + + @@ -958,6 +1283,7 @@ + @@ -974,6 +1300,7 @@ + @@ -984,9 +1311,8 @@ - - + @@ -996,6 +1322,7 @@ + @@ -1003,25 +1330,133 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + + + + + @@ -1042,13 +1477,16 @@ + + + @@ -1056,6 +1494,8 @@ + + @@ -1077,13 +1517,23 @@ + + + + + + + + + + @@ -1095,6 +1545,7 @@ + @@ -1104,6 +1555,7 @@ + @@ -1113,8 +1565,10 @@ + + @@ -1145,10 +1599,16 @@ + + + + + + @@ -1157,6 +1617,12 @@ + + + + + + @@ -1166,18 +1632,29 @@ + + + + + + + + + + + @@ -1187,48 +1664,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1239,9 +1760,11 @@ + + @@ -1250,42 +1773,8 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1293,175 +1782,223 @@ + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - + + + + @@ -1472,6 +2009,7 @@ + @@ -1487,9 +2025,73 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1513,6 +2115,7 @@ + @@ -1520,7 +2123,12 @@ + + + + + @@ -1530,12 +2138,20 @@ + + + + + + + + @@ -1543,14 +2159,18 @@ + + + + @@ -1570,6 +2190,7 @@ + @@ -1591,43 +2212,58 @@ + + + + + + + + + + + - - - + + + + + + - - + + + @@ -1636,6 +2272,8 @@ + + @@ -1644,6 +2282,8 @@ + + @@ -1673,6 +2313,8 @@ + + @@ -1692,7 +2334,9 @@ - + + + @@ -1706,9 +2350,17 @@ + + + + + + + + @@ -1722,7 +2374,9 @@ + + @@ -1736,9 +2390,12 @@ + + + @@ -1748,7 +2405,11 @@ + + + + @@ -1756,15 +2417,68 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1777,15 +2491,17 @@ - + + + @@ -1793,9 +2509,12 @@ + + + @@ -1805,63 +2524,42 @@ - + + + + - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - + + + - - - + + @@ -1874,6 +2572,8 @@ + + @@ -1887,6 +2587,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1899,6 +2641,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1906,34 +2701,27 @@ - + + + - - - - - - - - - - - + + - 7.1.0 + 8.1.0 1.4.0 @@ -1942,9 +2730,18 @@ swoole - - - - + + + + + + + + + + + + + diff --git a/php_swoole.h b/php_swoole.h new file mode 100644 index 00000000000..ce78ee064b6 --- /dev/null +++ b/php_swoole.h @@ -0,0 +1,79 @@ +/* + +----------------------------------------------------------------------+ + | 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_H +#define PHP_SWOOLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "php.h" +#include "php_ini.h" +#include "php_globals.h" +#include "php_main.h" + +#include "php_streams.h" +#include "php_network.h" + +#include "zend_variables.h" +#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 + +PHP_MINIT_FUNCTION(swoole); +PHP_MSHUTDOWN_FUNCTION(swoole); +PHP_RINIT_FUNCTION(swoole); +PHP_RSHUTDOWN_FUNCTION(swoole); +PHP_MINFO_FUNCTION(swoole); + +// clang-format off +ZEND_BEGIN_MODULE_GLOBALS(swoole) + zend_bool display_errors; + zend_bool cli; + zend_bool use_shortname; + 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 + +extern ZEND_DECLARE_MODULE_GLOBALS(swoole); + +#ifdef ZTS +#define SWOOLE_G(v) TSRMG(swoole_globals_id, zend_swoole_globals *, v) +#else +#define SWOOLE_G(v) (swoole_globals.v) +#endif + +#endif /* PHP_SWOOLE_H */ 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 88% rename from travis/run-tests.sh rename to scripts/run-tests.sh index 96bc9f2034e..7226694273f 100755 --- a/travis/run-tests.sh +++ b/scripts/run-tests.sh @@ -9,7 +9,11 @@ cd ${__DIR__} && cd ../tests/ # initialization echo "" && echo "⭐️ Initialization for tests..." && echo "" -./init +php ./init +cd ./include/lib +echo "composer update" +composer update +cd - echo "" # debug @@ -55,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/scripts/simple-compile-on-github.sh b/scripts/simple-compile-on-github.sh new file mode 100755 index 00000000000..a67e0723adc --- /dev/null +++ b/scripts/simple-compile-on-github.sh @@ -0,0 +1,25 @@ +#!/bin/sh -e +# shellcheck disable=SC2034 +# shellcheck disable=SC2006 +__CURRENT__=`pwd` +__DIR__=$(cd "$(dirname "$0")";pwd) + +if [ "${GITHUB_ACTIONS}" = true ]; then + # shellcheck disable=SC2028 + echo "\n❌ This script is just for Github!" + exit 255 +fi + +sudo apt-get update -y +sudo apt-get install -y libcurl4-openssl-dev libc-ares-dev + +cd "${__DIR__}" && cd ../ && \ +./clear.sh > /dev/null && \ +phpize --clean > /dev/null && \ +phpize > /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 && \ +php --ri curl && \ +php -d extension=swoole.so --ri swoole 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 5130e7cbd81..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 | +----------------------------------------------------------------------+ */ @@ -22,23 +22,15 @@ #include #include -#include - -#ifdef HAVE_EXECINFO -#include -#endif #ifdef __MACH__ #include #endif -#include #include #include #include #include -#include -#include #include "swoole_api.h" #include "swoole_string.h" @@ -49,13 +41,40 @@ #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) { +#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 int fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { return -1; @@ -73,25 +92,18 @@ static ssize_t getrandom(void *buffer, size_t size, unsigned int __flags) { close(fd); return read_bytes; +#endif } #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; @@ -113,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; @@ -123,11 +153,16 @@ 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(); + // DNS options + SwooleG.dns_tries = 1; + SwooleG.dns_resolvconf_path = SW_DNS_RESOLV_CONF; + // get system uname uname(&SwooleG.uname); // random seed @@ -149,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); @@ -157,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; } @@ -228,17 +251,80 @@ void swoole_clean(void) { delete g_logger_instance; g_logger_instance = nullptr; } + if (SwooleTG.buffer_stack) { + 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 = {}; } +SW_API void swoole_set_log_level(int level) { + if (sw_logger()) { + sw_logger()->set_level(level); + } +} + +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) { + 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(), ':'))) { + dns_server_port = atoi(_port + 1); + if (dns_server_port <= 0 || dns_server_port > 65535) { + dns_server_port = SW_DNS_SERVER_PORT; + } + dns_server_host[_port - server.c_str()] = '\0'; + } + SwooleG.dns_server_host = dns_server_host; + SwooleG.dns_server_port = dns_server_port; +} + +SW_API std::pair swoole_get_dns_server() { + std::pair result; + if (SwooleG.dns_server_host.empty()) { + result.first = ""; + result.second = 0; + } else { + result.first = SwooleG.dns_server_host; + result.second = SwooleG.dns_server_port; + } + return result; +} + 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; } @@ -246,24 +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.aio_init) { - printf("aio_init=%d, aio_task_num=%d, reactor=%p\n", - SwooleTG.aio_init, - SwooleTG.aio_task_num, - SwooleTG.reactor); - swFatalError(SW_ERROR_OPERATION_NOT_SUPPORT, "can not create server after using async file operation"); + if (SwooleTG.async_threads) { + 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) { @@ -279,7 +377,7 @@ pid_t swoole_fork(int flags) { /** * [!!!] All timers and event loops must be cleaned up after fork */ - if (SwooleTG.timer) { + if (swoole_timer_is_available()) { swoole_timer_free(); } if (SwooleG.memory_pool) { @@ -291,9 +389,9 @@ pid_t swoole_fork(int flags) { // reopen log file sw_logger()->reopen(); // reset eventLoop - if (SwooleTG.reactor) { + 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 { /** @@ -304,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]); @@ -344,7 +459,6 @@ void swoole_dump_hex(const char *data, size_t outlen) { } printf("\n"); } -#endif /** * Recursive directory creation @@ -355,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); @@ -370,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; } } @@ -451,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) { @@ -483,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)); @@ -491,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"); } } @@ -632,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; @@ -685,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)]; @@ -698,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; @@ -730,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"); @@ -746,6 +868,7 @@ void swoole_print_backtrace(void) { std::cout << boost::stacktrace::stacktrace(); } #elif defined(HAVE_EXECINFO) +#include void swoole_print_backtrace(void) { int size = 16; void *array[16]; @@ -759,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; @@ -773,32 +894,54 @@ 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); } -size_t swDataHead::dump(char *_buf, size_t _len) { +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); } -namespace swoole { -//------------------------------------------------------------------------------- std::string dirname(const std::string &file) { size_t index = file.find_last_of('/'); if (index == std::string::npos) { @@ -825,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); @@ -835,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 739cf49cd08..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 | +----------------------------------------------------------------------+ */ @@ -27,7 +27,7 @@ namespace swoole { #define SW_CHANNEL_MIN_MEM (1024 * 64) -struct Channel_item { +struct ChannelSlice { int length; char data[0]; }; @@ -41,13 +41,13 @@ Channel *Channel::make(size_t size, size_t maxlen, int flags) { /** * overflow space */ - mem = sw_shm_malloc(size + sizeof(Channel) + maxlen + sizeof(Channel_item)); + mem = sw_shm_malloc(size + sizeof(Channel) + maxlen + sizeof(ChannelSlice)); } else { - mem = sw_malloc(size + sizeof(Channel) + maxlen + sizeof(Channel_item)); + mem = sw_malloc(size + sizeof(Channel) + maxlen + sizeof(ChannelSlice)); } 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; } @@ -88,7 +88,7 @@ int Channel::in(const void *in_data, int data_length) { if (full()) { return SW_ERR; } - Channel_item *item; + ChannelSlice *item; int msize = sizeof(item->length) + data_length; if (tail < head) { @@ -96,10 +96,10 @@ int Channel::in(const void *in_data, int data_length) { if ((head - tail) < msize) { return SW_ERR; } - item = (Channel_item *) ((char *) mem + tail); + item = (ChannelSlice *) ((char *) mem + tail); tail += msize; } else { - item = (Channel_item *) ((char *) mem + tail); + item = (ChannelSlice *) ((char *) mem + tail); tail += msize; if (tail >= (off_t) size) { tail = 0; @@ -121,7 +121,7 @@ int Channel::out(void *out_buf, int buffer_length) { return SW_ERR; } - Channel_item *item = (Channel_item *) ((char *) mem + head); + ChannelSlice *item = (ChannelSlice *) ((char *) mem + head); assert(buffer_length >= item->length); memcpy(out_buf, item->data, item->length); head += (item->length + sizeof(item->length)); @@ -144,7 +144,7 @@ int Channel::peek(void *out, int buffer_length) { int length; lock->lock(); - Channel_item *item = (Channel_item *) ((char *) mem + head); + ChannelSlice *item = (ChannelSlice *) ((char *) mem + head); assert(buffer_length >= item->length); memcpy(out, item->data, item->length); length = item->length; 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 b44ea03858f..4fdc154ab28 100644 --- a/src/core/error.cc +++ b/src/core/error.cc @@ -10,25 +10,20 @@ | to obtain it through the world-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.h" -namespace swoole { +#include -class Exception { - public: - int code; - const char *msg; +static std::unordered_set ignored_errors; - Exception(int code) : code(code) { - msg = swoole_strerror(code); - } -}; +namespace swoole { +Exception::Exception(int code) throw() : code(code) { + msg = swoole_strerror(code); +} } // namespace swoole const char *swoole_strerror(int code) { @@ -36,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: @@ -56,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: @@ -68,10 +72,16 @@ const char *swoole_strerror(int code) { return "DNS Lookup resolve failed"; case SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT: return "DNS Lookup resolve timeout"; + case SW_ERROR_DNSLOOKUP_UNSUPPORTED: + return "DNS Lookup unsupported"; + case SW_ERROR_DNSLOOKUP_NO_SERVER: + return "DNS Lookup no server"; case SW_ERROR_BAD_IPV6_ADDRESS: 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: @@ -116,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: @@ -130,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: @@ -160,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: @@ -168,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: @@ -190,8 +216,22 @@ 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: + return "Server worker abnormal pipe data"; + case SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA: + return "Server worker unprocessed data"; case SW_ERROR_CO_OUT_OF_COROUTINE: return "Coroutine out of coroutine"; case SW_ERROR_CO_HAS_BEEN_BOUND: @@ -220,6 +260,16 @@ const char *swoole_strerror(int code) { return "Coroutine std thread link error"; case SW_ERROR_CO_DISABLED_MULTI_THREAD: return "Coroutine disabled multi thread"; + case SW_ERROR_CO_CANNOT_CANCEL: + return "Coroutine cannot cancel"; + case SW_ERROR_CO_NOT_EXISTS: + return "Coroutine not exists"; + case SW_ERROR_CO_CANCELED: + 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__ @@ -229,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 d41b2af1d7e..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 | +----------------------------------------------------------------------+ */ @@ -25,6 +25,38 @@ namespace swoole { +std::string Logger::get_pretty_name(const std::string &pretty_function, bool strip) { + size_t brackets = pretty_function.find_first_of("("); + if (brackets == pretty_function.npos) { + return ""; + } + + size_t begin = pretty_function.substr(0, brackets).rfind(" ") + 1; + size_t end = brackets - begin; + if (!strip) { + return pretty_function.substr(begin, end); + } + + auto method_name = pretty_function.substr(begin, end); + size_t count = 0, index = method_name.length(); + while (true) { + index = method_name.rfind("::", index); + if (index == method_name.npos) { + if (count == 1) { + return method_name.substr(method_name.rfind("::") + 2); + } + break; + } + count++; + if (count == 2) { + return method_name.substr(index + 2); + } + index -= 2; + } + + return method_name; +} + bool Logger::open(const char *_log_file) { if (opened) { close(); @@ -84,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); @@ -256,8 +288,8 @@ void Logger::put(int level, const char *content, size_t length) { if (date_with_microseconds) { auto now_us = std::chrono::duration_cast(now.time_since_epoch()).count(); - l_data_str += - sw_snprintf(date_str + l_data_str, SW_LOG_DATE_STRLEN - l_data_str, "<.%lld>", (long long) now_us - now_sec * 1000000); + l_data_str += sw_snprintf( + date_str + l_data_str, SW_LOG_DATE_STRLEN - l_data_str, "<.%lld>", (long long) now_us - now_sec * 1000000); } char process_flag = '@'; @@ -296,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 f117852d642..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=%d", 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 24aee783ace..21798968cba 100644 --- a/src/core/timer.cc +++ b/src/core/timer.cc @@ -10,14 +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_api.h" #include "swoole_reactor.h" #include "swoole_timer.h" -#include "swoole_util.h" + #if !defined(HAVE_CLOCK_GETTIME) && defined(__MACH__) #include #include @@ -29,7 +29,7 @@ static double orwl_timebase = 0.0; static uint64_t orwl_timestart = 0; -int swoole_clock_gettime(clock_id_t 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}; @@ -47,8 +47,7 @@ int swoole_clock_gettime(clock_id_t which_clock, struct timespec *t) { namespace swoole { -Timer::Timer() - : heap(1024, Heap::MIN_HEAP) { +Timer::Timer() : heap(1024, Heap::MIN_HEAP) { _current_id = -1; next_msec_ = -1; _next_id = 1; @@ -61,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; @@ -78,15 +85,19 @@ 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 *) { swoole_timer_free(); }); + reactor->add_destroy_callback([](void *) { + if (swoole_timer_is_available()) { + swoole_timer_free(); + } + }); return true; } void Timer::reinit(Reactor *reactor) { - init_reactor(reactor); + init_with_reactor(reactor); reactor->timeout_msec = next_msec_; } @@ -138,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=%u", - 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; } @@ -154,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=%u", - 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))) { @@ -171,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=%u", - 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; } @@ -190,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; @@ -200,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=%u", - 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; @@ -215,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; } @@ -222,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 d2e351b8312..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,69 +19,133 @@ namespace swoole { -Coroutine *Coroutine::current = nullptr; -long Coroutine::last_cid = 0; -std::unordered_map Coroutine::coroutines; -uint64_t Coroutine::peak_num = 0; +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 { +void thread_context_init(); +void thread_context_clean(); +} // namespace coroutine +#endif + +void Coroutine::activate() { +#ifdef SW_USE_THREAD_CONTEXT + coroutine::thread_context_init(); +#endif + activated = true; + on_bailout = nullptr; +} + +void Coroutine::deactivate() { +#ifdef SW_USE_THREAD_CONTEXT + 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() { SW_ASSERT(current == this || on_bailout != nullptr); state = STATE_WAITING; - if (sw_likely(on_yield)) { + resume_code_ = RC_OK; + if (sw_likely(on_yield && task)) { on_yield(task); } current = origin; + + CALC_EXECUTE_USEC(this, current); ctx.swap_out(); } +void Coroutine::yield(CancelFunc *cancel_fn) { + set_cancel_fn(cancel_fn); + yield(); + set_cancel_fn(nullptr); +} + +bool Coroutine::yield_ex(double timeout) { + TimerNode *timer = nullptr; + TimerCallback timer_callback = [this](Timer *timer, TimerNode *tnode) { + resume_code_ = RC_TIMEDOUT; + resume(); + }; + + if (timeout > 0) { + timer = swoole_timer_add(timeout, false, timer_callback, nullptr); + } + + CancelFunc cancel_fn = [](Coroutine *co) { + co->resume(); + return true; + }; + + yield(&cancel_fn); + + if (is_timedout()) { + swoole_set_last_error(SW_ERROR_CO_TIMEDOUT); + return false; + } + if (timer) { + swoole_timer_del(timer); + } + if (is_canceled()) { + swoole_set_last_error(SW_ERROR_CO_CANCELED); + return false; + } + return true; +} + void Coroutine::resume() { SW_ASSERT(current != this); if (sw_unlikely(on_bailout)) { return; } state = STATE_RUNNING; - if (sw_likely(on_resume)) { + if (sw_likely(on_resume && task)) { on_resume(task); } origin = current; current = this; + + CALC_EXECUTE_USEC(origin, 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; +bool Coroutine::cancel() { + if (!cancel_fn_) { + swoole_set_last_error(SW_ERROR_CO_CANNOT_CANCEL); + return false; } - state = STATE_RUNNING; - origin = current; - current = this; - ctx.swap_in(); - check_end(); + auto fn = *cancel_fn_; + set_cancel_fn(nullptr); + resume_code_ = RC_CANCELED; + return fn(this); } void Coroutine::close() { SW_ASSERT(current == this); state = STATE_END; - if (on_close) { + if (on_close && task) { 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; @@ -129,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 @@ -147,15 +211,18 @@ 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 { -bool run(const coroutine_func_t &fn, void *arg) { +bool run(const CoroutineFunc &fn, void *arg) { if (swoole_event_init(SW_EVENTLOOP_WAIT_EXIT) < 0) { return false; } + Coroutine::activate(); long cid = Coroutine::create(fn, arg); swoole_event_wait(); + Coroutine::deactivate(); return cid > 0; } } // namespace coroutine @@ -169,16 +236,29 @@ long swoole_coroutine_get_current_id() { return swoole::Coroutine::get_current_cid(); } +swoole::Coroutine *swoole_coroutine_get(long cid) { + auto i = swoole::Coroutine::coroutines.find(cid); + if (i == swoole::Coroutine::coroutines.end()) { + return nullptr; + } else { + return i->second; + } +} + +size_t swoole_coroutine_count() { + return swoole::Coroutine::coroutines.size(); +} + /** * for gdb */ static std::unordered_map::iterator _gdb_iterator; -void swoole_coro_iterator_reset() { +void swoole_coroutine_iterator_reset() { _gdb_iterator = swoole::Coroutine::coroutines.begin(); } -swoole::Coroutine *swoole_coro_iterator_each() { +swoole::Coroutine *swoole_coroutine_iterator_each() { if (_gdb_iterator == swoole::Coroutine::coroutines.end()) { return nullptr; } else { @@ -187,16 +267,3 @@ swoole::Coroutine *swoole_coro_iterator_each() { return co; } } - -swoole::Coroutine *swoole_coro_get(long cid) { - auto i = swoole::Coroutine::coroutines.find(cid); - if (i == swoole::Coroutine::coroutines.end()) { - return nullptr; - } else { - return i->second; - } -} - -size_t swoole_coro_count() { - return swoole::Coroutine::coroutines.size(); -} diff --git a/src/coroutine/channel.cc b/src/coroutine/channel.cc index 9762a4c9184..f0806728e78 100644 --- a/src/coroutine/channel.cc +++ b/src/coroutine/channel.cc @@ -10,14 +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_channel.h" -#include - namespace swoole { namespace coroutine { @@ -33,21 +31,31 @@ 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()); } - co->yield(); + Coroutine::CancelFunc cancel_fn = [this, type](Coroutine *co) { + if (type == CONSUMER) { + consumer_remove(co); + } else { + producer_remove(co); + } + co->resume(); + return true; + }; + co->yield(&cancel_fn); } void *Channel::pop(double timeout) { Coroutine *current_co = Coroutine::get_current_safe(); if (closed && is_empty()) { + error_ = ERROR_CLOSED; return nullptr; } if (is_empty() || !consumer_queue.empty()) { @@ -55,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); @@ -67,7 +74,16 @@ void *Channel::pop(double timeout) { if (msg.timer) { swoole_timer_del(msg.timer); } - if (msg.error || (closed && is_empty())) { + if (current_co->is_canceled()) { + error_ = ERROR_CANCELED; + return nullptr; + } + if (msg.error) { + error_ = ERROR_TIMEOUT; + return nullptr; + } + if (closed && is_empty()) { + error_ = ERROR_CLOSED; return nullptr; } } @@ -89,6 +105,7 @@ void *Channel::pop(double timeout) { bool Channel::push(void *data, double timeout) { Coroutine *current_co = Coroutine::get_current_safe(); if (closed) { + error_ = ERROR_CLOSED; return false; } if (is_full() || !producer_queue.empty()) { @@ -96,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); @@ -108,7 +124,16 @@ bool Channel::push(void *data, double timeout) { if (msg.timer) { swoole_timer_del(msg.timer); } - if (msg.error || closed) { + if (current_co->is_canceled()) { + error_ = ERROR_CANCELED; + return false; + } + if (msg.error) { + error_ = ERROR_TIMEOUT; + return false; + } + if (closed) { + error_ = ERROR_CLOSED; return false; } } @@ -116,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 */ @@ -131,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 75ff4089dec..8a6c10a9143 100644 --- a/src/coroutine/context.cc +++ b/src/coroutine/context.cc @@ -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 | +----------------------------------------------------------------------+ */ #include "swoole_coroutine_context.h" -#if __linux__ + +#ifdef SW_CONTEXT_PROTECT_STACK_PAGE #include +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif #endif #ifndef SW_USE_THREAD_CONTEXT @@ -24,34 +28,36 @@ #define MAGIC_STRING "swoole_coroutine#5652a7fb2b38be" #define START_OFFSET (64 * 1024) -#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) -#define MAP_ANONYMOUS MAP_ANON -#endif - namespace swoole { namespace coroutine { -Context::Context(size_t stack_size, const coroutine_func_t &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 - stack_ = (char *) ::mmap(0, stack_size_, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + int mapflags = MAP_PRIVATE | MAP_ANONYMOUS; +#ifdef __OpenBSD__ + // no-op for Linux and NetBSD, not to enable on FreeBSD as the semantic differs. + // However necessary on OpenBSD. + mapflags |= MAP_STACK; +#endif + stack_ = (char *) ::mmap(0, stack_size_, PROT_READ | PROT_WRITE, mapflags, -1, 0); #else 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_); @@ -62,7 +68,7 @@ Context::Context(size_t stack_size, const coroutine_func_t &fn, void *private_da 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 @@ -81,7 +87,7 @@ Context::Context(size_t stack_size, const coroutine_func_t &fn, void *private_da 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 @@ -114,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 f184a172b93..af040bbdc12 100644 --- a/src/coroutine/file_lock.cc +++ b/src/coroutine/file_lock.cc @@ -10,11 +10,10 @@ | to obtain it through the world-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 #include diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index c358f900fcf..1649d4302a5 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -10,40 +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 | +----------------------------------------------------------------------+ */ #include -#include #include #include #include #include #include -#include -#include #include #include #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()) { @@ -52,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); } @@ -69,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; @@ -81,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) { @@ -117,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); @@ -152,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++; } @@ -179,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())) { @@ -187,22 +196,65 @@ 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; +} + +int swoole_coroutine_socket_create(int fd) { + if (sw_unlikely(is_no_coro())) { + return -1; + } + auto socket = std::make_shared(fd, SW_SOCK_RAW); + int _fd = socket->get_fd(); + if (sw_unlikely(_fd < 0)) { + 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; +} + +uint8_t swoole_coroutine_socket_exists(int fd) { + return socket_map.find(fd) != socket_map.end(); +} + ssize_t swoole_coroutine_read(int sockfd, void *buf, size_t count) { if (sw_unlikely(is_no_coro())) { 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; } @@ -211,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; } @@ -227,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; } @@ -237,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; } @@ -247,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; } @@ -257,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; } @@ -267,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; } @@ -277,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; } @@ -287,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; } @@ -297,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; } @@ -307,7 +379,97 @@ 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; +} + +FILE *swoole_coroutine_fopen(const char *pathname, const char *mode) { + if (sw_unlikely(is_no_coro())) { + return fopen(pathname, mode); + } + + FILE *retval = nullptr; + async([&]() { retval = fopen(pathname, mode); }); + return retval; +} + +FILE *swoole_coroutine_fdopen(int fd, const char *mode) { + if (sw_unlikely(is_no_coro())) { + return fdopen(fd, mode); + } + + FILE *retval = nullptr; + async([&]() { retval = fdopen(fd, mode); }); + return retval; +} + +FILE *swoole_coroutine_freopen(const char *pathname, const char *mode, FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return freopen(pathname, mode, stream); + } + + FILE *retval = nullptr; + async([&]() { retval = freopen(pathname, mode, stream); }); + return retval; +} + +size_t swoole_coroutine_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return fread(ptr, size, nmemb, stream); + } + + size_t retval = 0; + async([&]() { retval = fread(ptr, size, nmemb, stream); }); + return retval; +} + +size_t swoole_coroutine_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return fwrite(ptr, size, nmemb, stream); + } + + size_t retval = 0; + async([&]() { retval = fwrite(ptr, size, nmemb, stream); }); + return retval; +} + +char *swoole_coroutine_fgets(char *s, int size, FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return fgets(s, size, stream); + } + + char *retval = nullptr; + async([&]() { retval = fgets(s, size, stream); }); + return retval; +} + +int swoole_coroutine_fputs(const char *s, FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return fputs(s, stream); + } + + int retval = -1; + async([&]() { retval = fputs(s, stream); }); + return retval; +} + +int swoole_coroutine_feof(FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return feof(stream); + } + + int retval = -1; + async([&]() { retval = feof(stream); }); + return retval; +} + +int swoole_coroutine_fclose(FILE *stream) { + if (sw_unlikely(is_no_coro())) { + return fclose(stream); + } + + int retval = -1; + async([&]() { retval = fclose(stream); }); return retval; } @@ -317,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; } @@ -327,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; } @@ -338,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; } @@ -351,7 +511,7 @@ int swoole_coroutine_closedir(DIR *dirp) { } int retval = -1; - swoole::coroutine::async([&]() { retval = closedir(dirp); }); + async([&]() { retval = closedir(dirp); }); return retval; } @@ -364,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; } @@ -381,15 +541,25 @@ int swoole_coroutine_socket_set_timeout(int sockfd, int which, double timeout) { } } +int swoole_coroutine_socket_set_connect_timeout(int sockfd, double timeout) { + auto socket = get_socket_ex(sockfd); + if (sw_unlikely(socket == nullptr)) { + errno = EINVAL; + return -1; + } + socket->set_timeout(timeout, Socket::TIMEOUT_DNS | Socket::TIMEOUT_CONNECT); + return 0; +} + 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; } @@ -399,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; }); @@ -414,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 0269e752fd8..464780b3b21 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -13,25 +13,23 @@ | @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 "swoole_coroutine_socket.h" - -#include -#include - +#include "swoole_string.h" #include "swoole_util.h" -#include "swoole_socket.h" -#include "swoole_coroutine_system.h" +#include "swoole_reactor.h" #include "swoole_buffer.h" #include "swoole_base64.h" +#include "swoole_coroutine_socket.h" +#include "swoole_coroutine_system.h" + namespace swoole { namespace coroutine { -enum Socket::TimeoutType Socket::timeout_type_list[4] = { TIMEOUT_DNS, TIMEOUT_CONNECT, TIMEOUT_READ, TIMEOUT_WRITE }; +enum Socket::TimeoutType Socket::timeout_type_list[4] = {TIMEOUT_DNS, TIMEOUT_CONNECT, TIMEOUT_READ, TIMEOUT_WRITE}; void Socket::timer_callback(Timer *timer, TimerNode *tnode) { Socket *socket = (Socket *) tnode->data; @@ -101,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) { @@ -114,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. @@ -121,9 +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); @@ -143,21 +173,19 @@ 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); }; + if (sw_likely(event == SW_EVENT_READ)) { read_co = co; - read_co->yield(); + read_co->yield(&cancel_fn); read_co = nullptr; - } else // if (event == SW_EVENT_WRITE) - { + } else if (event == SW_EVENT_WRITE) { if (sw_unlikely(!zero_copy && __n > 0 && *__buf != get_write_buffer()->str)) { write_buffer->clear(); if (write_buffer->append((const char *) *__buf, __n) != SW_OK) { @@ -167,8 +195,11 @@ bool Socket::wait_event(const enum swEvent_type event, const void **__buf, size_ *__buf = write_buffer->str; } write_co = co; - write_co->yield(); + write_co->yield(&cancel_fn); write_co = nullptr; + } else { + assert(0); + return false; } _failed: #ifdef SW_USE_OPENSSL @@ -186,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() { @@ -218,7 +247,8 @@ bool Socket::socks5_handshake() { return false; } if (method != ctx->method) { - swoole_error_log(SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_METHOD, "SOCKS authentication method is not supported"); + swoole_error_log( + SW_LOG_NOTICE, SW_ERROR_SOCKS5_UNSUPPORT_METHOD, "SOCKS authentication method is not supported"); return false; } // authentication @@ -312,27 +342,19 @@ bool Socket::socks5_handshake() { ctx->state = SW_SOCKS5_STATE_READY; return true; } else { - swoole_error_log( - SW_LOG_NOTICE, SW_ERROR_SOCKS5_SERVER_ERROR, "Socks5 server error, reason: %s", Socks5Proxy::strerror(result)); + swoole_error_log(SW_LOG_NOTICE, + SW_ERROR_SOCKS5_SERVER_ERROR, + "Socks5 server error, reason: %s", + Socks5Proxy::strerror(result)); return false; } } 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 @@ -341,43 +363,11 @@ bool Socket::http_proxy_handshake() { send_buffer->clear(); }; - if (!http_proxy->password.empty()) { - char auth_buf[256]; - char encode_buf[512]; - n = sw_snprintf(auth_buf, - sizeof(auth_buf), - "%.*s:%.*s", - (int) http_proxy->username.length(), - http_proxy->username.c_str(), - (int) http_proxy->password.length(), - http_proxy->password.c_str()); - base64_encode((unsigned char *) auth_buf, n, encode_buf); - 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, - encode_buf); - } 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; } @@ -392,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)); + std::string("wrong http_proxy response received, \n[Request]: ") + send_buffer->to_std_string() + + "\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; } @@ -466,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; @@ -478,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; @@ -493,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; } @@ -524,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; @@ -580,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; @@ -632,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; @@ -646,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; } @@ -674,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; } @@ -700,11 +678,11 @@ 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 { - set_err(EINVAL, "unknow protocol[%d]"); + set_err(EINVAL, "unknown protocol[%d]"); return false; } } @@ -715,6 +693,7 @@ bool Socket::connect(std::string _host, int _port, int flags) { if (connect(_target_addr, socket->info.len) == false) { return false; } + // socks5 proxy if (socks5_proxy && socks5_handshake() == false) { if (errCode == 0) { @@ -744,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()) { @@ -758,15 +737,19 @@ bool Socket::check_liveness() { ssize_t Socket::peek(void *__buf, size_t __n) { ssize_t retval = socket->peek(__buf, __n, 0); - set_err(retval < 0 ? errno : 0); + check_return_value(retval); 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 { @@ -782,8 +765,8 @@ 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)); - set_err(retval < 0 ? errno : 0); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + check_return_value(retval); return retval; } @@ -795,9 +778,9 @@ 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)); - set_err(retval < 0 ? errno : 0); + check_return_value(retval); return retval; } @@ -809,11 +792,37 @@ 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)); - set_err(retval < 0 ? errno : 0); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + check_return_value(retval); return retval; } +ssize_t Socket::recv_line(void *__buf, size_t maxlen) { + size_t n = 0; + ssize_t m = 0; + char *t = (char *) __buf; + + *t = '\0'; + while (*t != '\n' && *t != '\r' && n < maxlen) { + if (m > 0) { + t++; + n++; + } + if (n < maxlen) { + m = recv_with_buffer((void *) t, 1); + if (m < 0) { + return -1; + } else if (m == 0) { + return n > 0 ? n : 0; + } + } + } + if (n < maxlen) { + n++; + } + return n; +} + ssize_t Socket::recv_with_buffer(void *__buf, size_t __n) { if (sw_unlikely(!is_available(SW_EVENT_READ))) { return -1; @@ -858,9 +867,9 @@ 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)); - set_err(retval < 0 ? errno : 0); + check_return_value(retval); return retval; } @@ -872,8 +881,8 @@ 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)); - set_err(retval < 0 ? errno : 0); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + check_return_value(retval); return retval; } @@ -886,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; } @@ -914,12 +923,12 @@ 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; if (timer.start() && wait_event(SW_EVENT_READ)) { - set_err(retval < 0 ? errno : 0); + check_return_value(retval); } recv_barrier = nullptr; @@ -934,8 +943,8 @@ 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)); - set_err(retval < 0 ? errno : 0); + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); + check_return_value(retval); return retval; } @@ -948,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; } @@ -976,12 +985,12 @@ 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; if (timer.start() && wait_event(SW_EVENT_WRITE)) { - set_err(retval < 0 ? errno : 0); + check_return_value(retval); } send_barrier = nullptr; @@ -1001,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; } @@ -1011,12 +1020,13 @@ 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; if (timer.start() && wait_event(SW_EVENT_READ)) { - set_err(retval < 0 ? errno : 0); + check_return_value(retval); } recv_barrier = nullptr; @@ -1036,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; } @@ -1046,12 +1056,13 @@ 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; if (timer.start() && wait_event(SW_EVENT_WRITE)) { - set_err(retval < 0 ? errno : 0); + check_return_value(retval); } send_barrier = nullptr; @@ -1066,8 +1077,8 @@ 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)); - set_err(retval < 0 ? errno : 0); + } while (retval < 0 && socket->catch_read_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_READ)); + check_return_value(retval); return retval; } @@ -1082,8 +1093,8 @@ 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)); - set_err(retval < 0 ? errno : 0); + } while (retval < 0 && socket->catch_write_error(errno) == SW_WAIT && timer.start() && wait_event(SW_EVENT_WRITE)); + check_return_value(retval); return retval; } @@ -1117,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; } @@ -1131,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); @@ -1146,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; @@ -1156,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; @@ -1206,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) { @@ -1228,7 +1252,7 @@ bool Socket::ssl_handshake() { } } } else { - enum swReturn_code retval; + ReturnCode retval; TimerController timer(&read_timer, read_timeout, this, timer_callback); do { @@ -1266,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(); @@ -1297,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; @@ -1341,7 +1366,7 @@ ssize_t Socket::sendto(const std::string &host, int port, const void *__buf, siz for (size_t i = 0; i < 2; i++) { if (type == SW_SOCK_UDP) { - if (::inet_aton(ip.c_str(), &addr.in.sin_addr) == 0) { + if (::inet_pton(AF_INET, ip.c_str(), &addr.in.sin_addr) == 0) { read_co = write_co = Coroutine::get_current_safe(); ip = System::gethostbyname(host, sock_domain, dns_timeout); read_co = write_co = nullptr; @@ -1388,10 +1413,10 @@ 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)))); - set_err(retval < 0 ? errno : 0); + check_return_value(retval); } return retval; @@ -1413,16 +1438,17 @@ 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)))); - set_err(retval < 0 ? errno : 0); + 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; } 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) { @@ -1444,20 +1470,28 @@ 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(); - set_err(SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, "remote packet is too big"); + 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); + set_err(SW_ERROR_PACKAGE_LENGTH_TOO_LARGE, sw_error); return -1; } @@ -1592,13 +1626,12 @@ ssize_t Socket::recv_packet(double timeout) { bool Socket::shutdown(int __how) { set_err(0); - if (!is_connect() || (__how == SHUT_RD && shutdown_read) || (__how == SHUT_WR && shutdown_write)) { + if (!is_connected() || (__how == SHUT_RD && shutdown_read) || (__how == SHUT_WR && shutdown_write)) { errno = ENOTCONN; } else { #ifdef SW_USE_OPENSSL if (socket->ssl) { - SSL_set_quiet_shutdown(socket->ssl, 0); - SSL_shutdown(socket->ssl); + socket->ssl_shutdown(); } #endif if (::shutdown(sock_fd, __how) == 0 || errno == ENOTCONN) { @@ -1636,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; } @@ -1649,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; } } @@ -1696,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); @@ -1710,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 = {}; @@ -1727,11 +1758,8 @@ 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(); } diff --git a/src/coroutine/system.cc b/src/coroutine/system.cc index 1683e17f5dc..cc6d5856eb8 100644 --- a/src/coroutine/system.cc +++ b/src/coroutine/system.cc @@ -10,25 +10,27 @@ | to obtain it through the world-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_system.h" +#include "swoole_coroutine_socket.h" #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 { -struct AsyncTask { - Coroutine *co; - AsyncEvent *original_event; -}; - 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; @@ -46,50 +48,55 @@ void System::clear_dns_cache() { } } -static void aio_onDNSCompleted(AsyncEvent *event) { - if (event->canceled) { - return; +static void sleep_callback(Coroutine *co, bool *canceled) { + if (*canceled == false) { + co->resume(); } - AsyncTask *task = (AsyncTask *) event->object; - task->original_event->ret = event->ret; - task->original_event->error = event->error; - ((Coroutine *) task->co)->resume(); -} - -static void aio_onDNSTimeout(Timer *timer, TimerNode *tnode) { - AsyncEvent *event = (AsyncEvent *) tnode->data; - event->canceled = 1; - AsyncTask *task = (AsyncTask *) event->object; - task->original_event->ret = -1; - task->original_event->error = SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT; - ((Coroutine *) task->co)->resume(); -} - -static void sleep_timeout(Timer *timer, TimerNode *tnode) { - ((Coroutine *) tnode->data)->resume(); + delete canceled; } int System::sleep(double sec) { Coroutine *co = Coroutine::get_current_safe(); + + bool *canceled = new bool(false); + TimerNode *tnode = nullptr; + if (sec < SW_TIMER_MIN_SEC) { - swoole_event_defer([co](void *data) { co->resume(); }, nullptr); - } else if (swoole_timer_add((long) (sec * 1000), false, sleep_timeout, co) == nullptr) { - return -1; + swoole_event_defer([co, canceled](void *data) { sleep_callback(co, canceled); }, nullptr); + } else { + auto fn = [canceled](Timer *timer, TimerNode *tnode) { sleep_callback((Coroutine *) tnode->data, canceled); }; + tnode = swoole_timer_add((long) (sec * 1000), false, fn, co); + if (tnode == nullptr) { + delete canceled; + return -1; + } } - co->yield(); - return 0; + Coroutine::CancelFunc cancel_fn = [canceled, tnode](Coroutine *co) { + *canceled = true; + if (tnode) { + swoole_timer_del(tnode); + } + co->resume(); + return true; + }; + co->yield(&cancel_fn); + if (co->is_canceled()) { + swoole_set_last_error(SW_ERROR_CO_CANCELED); + return SW_ERR; + } + return SW_OK; } std::shared_ptr System::read_file(const char *file, bool lock) { std::shared_ptr result; - swoole::coroutine::async([&result, file, 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(); @@ -102,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; @@ -111,34 +118,56 @@ std::shared_ptr System::read_file(const char *file, bool lock) { ssize_t System::write_file(const char *file, char *buf, size_t length, bool lock, int flags) { ssize_t retval = -1; int file_flags = flags | O_CREAT | O_WRONLY; - swoole::coroutine::async([&]() { + 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; }); return retval; } +std::string gethostbyname_impl_with_async(const std::string &hostname, int domain, double timeout) { + AsyncEvent ev{}; + auto req = new GethostbynameRequest(hostname, domain); + ev.data = std::shared_ptr(req); + ev.retval = 1; + + coroutine::async(async::handler_gethostbyname, ev, timeout); + + if (ev.retval == -1) { + if (ev.error == SW_ERROR_AIO_TIMEOUT) { + ev.error = SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT; + } + swoole_set_last_error(ev.error); + return ""; + } else { + std::string addr(req->addr); + return addr; + } +} + std::string System::gethostbyname(const std::string &hostname, int domain, double timeout) { if (dns_cache == nullptr && dns_cache_capacity != 0) { dns_cache = new LRUCache(dns_cache_capacity); } std::string cache_key; + std::string result; + if (dns_cache) { cache_key.append(domain == AF_INET ? "4_" : "6_"); cache_key.append(hostname); @@ -149,64 +178,24 @@ std::string System::gethostbyname(const std::string &hostname, int domain, doubl } } - AsyncEvent ev{}; - AsyncTask task; - - if (hostname.size() < SW_IP_MAX_LENGTH) { - ev.nbytes = SW_IP_MAX_LENGTH + 1; - } else { - ev.nbytes = hostname.size() + 1; - } - - task.co = Coroutine::get_current_safe(); - task.original_event = &ev; - - ev.buf = sw_malloc(ev.nbytes); - if (!ev.buf) { - return ""; +#ifdef SW_USE_CARES + auto result_list = dns_lookup_impl_with_cares(hostname.c_str(), domain, timeout); + if (!result_list.empty()) { + if (SwooleG.dns_lookup_random) { + result = result_list[rand() % result_list.size()]; + } else { + result = result_list[0]; + } } +#else + result = gethostbyname_impl_with_async(hostname, domain, timeout); +#endif - memcpy(ev.buf, hostname.c_str(), hostname.size()); - ((char *) ev.buf)[hostname.size()] = 0; - ev.flags = domain; - ev.object = (void *) &task; - ev.handler = async::handler_gethostbyname; - ev.callback = aio_onDNSCompleted; - /* TODO: find a better way */ - ev.ret = 1; - - AsyncEvent *event = async::dispatch(&ev); - TimerNode *timer = nullptr; - if (timeout > 0) { - timer = swoole_timer_add((long) (timeout * 1000), false, aio_onDNSTimeout, event); - } - task.co->yield(); - if (ev.ret == 1) { - /* TODO: find a better way */ - /* canceled */ - event->canceled = 1; - ev.ret = -1; - ev.error = SW_ERROR_DNSLOOKUP_RESOLVE_FAILED; - } - if (timer) { - swoole_timer_del(timer); + if (dns_cache && !result.empty()) { + dns_cache->set(cache_key, std::make_shared(result), dns_cache_expire); } - if (ev.ret == -1) { - swoole_set_last_error(ev.error); - return ""; - } else { - if (dns_cache) { - std::string *addr = new std::string((char *) ev.buf); - dns_cache->set(cache_key, std::shared_ptr(addr), dns_cache_expire); - sw_free(ev.buf); - return *addr; - } - - std::string addr((char *) ev.buf); - sw_free(ev.buf); - return addr; - } + return result; } std::vector System::getaddrinfo( @@ -215,107 +204,103 @@ std::vector System::getaddrinfo( assert(family == AF_INET || family == AF_INET6); AsyncEvent ev{}; - network::GetaddrinfoRequest req{}; - - AsyncTask task{Coroutine::get_current_safe(), &ev}; - - ev.object = &task; - ev.handler = async::handler_getaddrinfo; - ev.callback = aio_onDNSCompleted; - ev.req = &req; + auto req = new GetaddrinfoRequest(hostname, family, socktype, protocol, service); + ev.data = std::shared_ptr(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; - - AsyncEvent *event = async::dispatch(&ev); - TimerNode *timer = nullptr; - if (timeout > 0) { - timer = swoole_timer_add((long) (timeout * 1000), false, aio_onDNSTimeout, event); - } - task.co->yield(); - if (timer) { - swoole_timer_del(timer); - } + coroutine::async(async::handler_getaddrinfo, ev, timeout); std::vector retval; - if (ev.ret == -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; } -bool System::wait_signal(int signo, double timeout) { - static Coroutine *listeners[SW_SIGNO_MAX]; - Coroutine *co = Coroutine::get_current_safe(); +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() + */ +int System::wait_signal(const std::vector &signals, double timeout) { + SignalListener listener = { + Coroutine::get_current_safe(), + -1, + }; if (SwooleTG.signal_listener_num > 0) { - errno = EBUSY; - return false; + swoole_set_last_error(EBUSY); + return -1; } - if (signo < 0 || signo >= SW_SIGNO_MAX || signo == SIGCHLD) { - errno = 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; }); - } - /* 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); + [](Reactor *reactor, size_t &event_num) -> bool { return SwooleTG.co_signal_listener_num == 0; }); } - co->yield(); + 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; - errno = ETIMEDOUT; - return false; } - if (timer) { - swoole_timer_del(timer); - } + SwooleTG.co_signal_listener_num--; - return true; + return retval ? listener.signo : -1; } struct CoroPollTask { @@ -360,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) { @@ -398,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) { @@ -411,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) { @@ -432,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; @@ -458,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; @@ -472,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(); @@ -485,13 +470,23 @@ struct EventWaiter { TimerNode *timer; Coroutine *co; int revents; + int error_; EventWaiter(int fd, int events, double timeout) { - revents = 0; - socket = swoole::make_socket(fd, SW_FD_CORO_EVENT); + error_ = revents = 0; + socket = swoole::make_socket(fd, SW_FD_CO_EVENT); socket->object = this; timer = nullptr; - co = nullptr; + co = Coroutine::get_current_safe(); + + Coroutine::CancelFunc cancel_fn = [this](Coroutine *) { + if (timer) { + swoole_timer_del(timer); + } + error_ = SW_ERROR_CO_CANCELED; + co->resume(); + return true; + }; if (swoole_event_add(socket, events) < 0) { swoole_set_last_error(errno); @@ -499,23 +494,25 @@ 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->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 = Coroutine::get_current(); - co->yield(); + co->yield(&cancel_fn); if (timer != nullptr) { swoole_timer_del(timer); - } else if (timeout > 0) { - swoole_set_last_error(ETIMEDOUT); + } + if (error_) { + swoole_set_last_error(error_); } swoole_event_del(socket); _done: @@ -524,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(); }); } @@ -546,6 +543,9 @@ static int event_waiter_error_callback(Reactor *reactor, Event *event) { return SW_OK; } +/** + * @errror: errno & swoole_get_last_error() + */ int System::wait_event(int fd, int events, double timeout) { events &= SW_EVENT_READ | SW_EVENT_WRITE; if (events == 0) { @@ -569,8 +569,13 @@ int System::wait_event(int fd, int events, double timeout) { return 0; } - int revents = EventWaiter(fd, events, timeout).revents; + EventWaiter waiter(fd, events, timeout); + if (waiter.error_) { + errno = swoole_get_last_error(); + return SW_ERR; + } + int revents = waiter.revents; if (revents & SW_EVENT_ERROR) { revents ^= SW_EVENT_ERROR; if (events & SW_EVENT_READ) { @@ -584,44 +589,65 @@ 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, async::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) { if (event->canceled) { return; } - AsyncTask *task = (AsyncTask *) event->object; - task->original_event->error = event->error; - task->original_event->ret = event->ret; - task->co->resume(); -} - -static void async_task_timeout(Timer *timer, TimerNode *tnode) { - AsyncEvent *event = (AsyncEvent *) tnode->data; - event->canceled = 1; - AsyncTask *task = (AsyncTask *) event->object; - task->original_event->error = SW_ERROR_AIO_TIMEOUT; - task->co->resume(); + Coroutine *co = (Coroutine *) event->object; + co->resume(); } +/** + * @error: swoole_get_last_error() + */ bool async(async::Handler handler, AsyncEvent &event, double timeout) { - AsyncTask task; - TimerNode *timer = nullptr; - - task.co = Coroutine::get_current_safe(); - task.original_event = &event; + Coroutine *co = Coroutine::get_current_safe(); - event.object = (void *) &task; + event.object = co; event.handler = handler; event.callback = async_task_completed; @@ -629,16 +655,16 @@ bool async(async::Handler handler, AsyncEvent &event, double timeout) { if (_ev == nullptr) { return false; } - if (timeout > 0) { - timer = swoole_timer_add((long) (timeout * 1000), false, async_task_timeout, _ev); - } - task.co->yield(); - if (event.error == SW_ERROR_AIO_TIMEOUT) { + + if (!co->yield_ex(timeout)) { + event.canceled = _ev->canceled = true; + event.retval = -1; + event.error = errno = swoole_get_last_error(); return false; } else { - if (timer) { - swoole_timer_del(timer); - } + event.canceled = _ev->canceled; + event.error = errno = _ev->error; + event.retval = _ev->retval; return true; } } @@ -652,19 +678,15 @@ static void async_lambda_handler(AsyncEvent *event) { AsyncLambdaTask *task = reinterpret_cast(event->object); task->fn(); event->error = errno; - event->ret = 0; + event->retval = 0; } 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) { - TimerNode *timer = nullptr; +bool async(const std::function &fn) { AsyncEvent event{}; AsyncLambdaTask task{Coroutine::get_current_safe(), fn}; @@ -676,20 +698,47 @@ bool async(const std::function &fn, double timeout) { if (_ev == nullptr) { return false; } - if (timeout > 0) { - timer = swoole_timer_add((long) (timeout * 1000), false, async_task_timeout, _ev); - } + task.co->yield(); errno = _ev->error; - swoole_set_last_error(_ev->error); - if (_ev->error == SW_ERROR_AIO_TIMEOUT) { - return false; - } else { - if (timer) { - swoole_timer_del(timer); + 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; } - return true; + 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 42aa42ba23f..b670d495719 100644 --- a/src/coroutine/thread_context.cc +++ b/src/coroutine/thread_context.cc @@ -10,11 +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 @@ -22,26 +24,45 @@ namespace swoole { namespace coroutine { -static std::mutex global_lock; +static std::mutex g_lock; static Reactor *g_reactor = nullptr; static Timer *g_timer = nullptr; +static String *g_buffer = nullptr; +static AsyncThreads *g_async_threads = nullptr; static std::mutex *current_lock = nullptr; -static void empty_timer(Timer *timer, TimerNode *tnode) { - // do nothing +void thread_context_init() { + if (!swoole_timer_is_available()) { + swoole_timer_add( + 1L, + false, + [](Timer *timer, TimerNode *tnode) { + // do nothing + }, + nullptr); + } + if (SwooleTG.async_threads == nullptr) { + SwooleTG.async_threads = new AsyncThreads(); + } + g_reactor = SwooleTG.reactor; + g_buffer = SwooleTG.buffer_stack; + g_timer = SwooleTG.timer; + g_async_threads = SwooleTG.async_threads; + current_lock = &g_lock; + g_lock.lock(); } -Context::Context(size_t stack_size, const coroutine_func_t &fn, void *private_data) - : fn_(fn), private_data_(private_data) { - if (sw_unlikely(current_lock == nullptr)) { - current_lock = &global_lock; - g_reactor = SwooleTG.reactor; - if (SwooleTG.timer == nullptr) { - swoole_timer_add(1, 0, empty_timer, nullptr); - } - g_timer = SwooleTG.timer; - global_lock.lock(); - } +void thread_context_clean() { + g_reactor = nullptr; + g_buffer = nullptr; + g_timer = nullptr; + g_async_threads = nullptr; + current_lock = nullptr; + g_lock.unlock(); +} + +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; @@ -67,15 +88,18 @@ 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; + SwooleTG.buffer_stack = g_buffer; + SwooleTG.async_threads = g_async_threads; _this->lock_.lock(); _this->fn_(_this->private_data_); - _this->lock_.unlock(); - _this->swap_lock_->unlock(); _this->end_ = true; + current_lock = _this->swap_lock_; + _this->swap_lock_->unlock(); } } // namespace coroutine } // namespace swoole 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 d186595d4a0..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) { @@ -40,29 +42,35 @@ Mutex::Mutex(int flags) : Lock() { pthread_mutexattr_init(&impl->attr_); if (flags & PROCESS_SHARED) { +#ifdef HAVE_PTHREAD_MUTEXATTR_SETPSHARED pthread_mutexattr_setpshared(&impl->attr_, PTHREAD_PROCESS_SHARED); +#else + swoole_warning("PTHREAD_MUTEX_PSHARED is not supported"); +#endif } if (flags & ROBUST) { #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 ed4cc7630ff..a6df5959d8f 100644 --- a/src/memory/buffer.cc +++ b/src/memory/buffer.cc @@ -10,42 +10,43 @@ | to obtain it through the world-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_buffer.h" + 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(); } @@ -61,6 +62,8 @@ void Buffer::append(const void *data, uint32_t size) { char *_pos = (char *) data; uint32_t _n; + assert(size > 0); + // buffer enQueue while (_length > 0) { _n = _length >= chunk_size ? chunk_size : _length; @@ -69,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; @@ -83,6 +86,8 @@ void Buffer::append(const struct iovec *iov, size_t iovcnt, off_t offset) { size_t _length = 0; SW_LOOP_N(iovcnt) { + assert(iov[i].iov_len > 0); + assert(iov[i].iov_base != nullptr); _length += iov[i].iov_len; } @@ -106,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; @@ -116,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 a8444bbeafb..d03837ea059 100644 --- a/src/memory/global_memory.cc +++ b/src/memory/global_memory.cc @@ -10,22 +10,20 @@ | to obtain it through the world-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_memory.h" + #include -#include #include #define SW_MIN_PAGE_SIZE 4096 namespace swoole { -struct MemoryBlock; - struct GlobalMemoryImpl { bool shared; uint32_t pagesize; @@ -41,6 +39,7 @@ struct GlobalMemoryImpl { struct MemoryBlock { uint32_t size; + uint32_t reserved; char memory[0]; }; @@ -55,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) { @@ -85,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; } @@ -94,17 +93,17 @@ 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; } } - block = (MemoryBlock *) impl->pages.back() + impl->alloc_offset; + block = (MemoryBlock *) (impl->pages.back() + impl->alloc_offset); impl->alloc_offset += alloc_size; block->size = size; @@ -121,6 +120,14 @@ void GlobalMemory::destroy() { } } +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 8170ad06e03..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,12 +35,12 @@ 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; } table->mutex = new Mutex(Mutex::PROCESS_SHARED); - table->iterator = new TableIterator; + table->iterator = nullptr; table->column_map = new std::unordered_map; table->column_list = new std::vector; table->size = rows_size; @@ -52,18 +52,16 @@ Table *Table::make(uint32_t rows_size, float conflict_proportion) { table->hash_func = swoole_hash_austin; #endif - sw_memset_zero(table->iterator, sizeof(TableIterator)); - return table; } 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); @@ -72,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 */ @@ -101,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); @@ -134,6 +142,8 @@ bool Table::create() { _memory = (char *) _memory + _row_memory_size * size; _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; @@ -193,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; } /** @@ -209,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(); @@ -217,37 +227,48 @@ void TableRow::lock() { } void Table::forward() { + iterator->lock(); for (; iterator->absolute_index < size; iterator->absolute_index++) { TableRow *row = get_by_index(iterator->absolute_index); if (row == nullptr) { continue; - } else if (row->next == nullptr) { + } + row->lock(); + if (row->next == nullptr) { iterator->absolute_index++; - iterator->row = row; + memcpy(iterator->current_, row, iterator->row_memory_size_); + row->unlock(); + iterator->unlock(); return; } else { uint32_t i = 0; + TableRow *tmp_row = row; for (;; i++) { - if (row == nullptr) { + if (tmp_row == nullptr) { iterator->collision_index = 0; break; } if (i == iterator->collision_index) { iterator->collision_index++; - iterator->row = row; + memcpy(iterator->current_, tmp_row, iterator->row_memory_size_); + row->unlock(); + iterator->unlock(); return; } - row = row->next; + tmp_row = tmp_row->next; } } + row->unlock(); } - iterator->row = nullptr; + sw_memset_zero(iterator->current_, sizeof(TableRow)); + iterator->unlock(); } TableRow *Table::get(const char *key, uint16_t keylen, TableRow **rowlock) { check_key_length(&keylen); TableRow *row = hash(key, keylen); + *rowlock = row; row->lock(); @@ -276,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) { - mutex->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 - mutex->unlock(); + TableRow *new_row = alloc_row(); if (!new_row) { return nullptr; } @@ -305,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 { @@ -319,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; } @@ -366,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; } - mutex->lock(); - tmp->clear(); - pool->free(tmp); - mutex->unlock(); + free_row(tmp); } _delete_element: + sw_atomic_fetch_add(&(delete_count), 1); sw_atomic_fetch_sub(&(row_num), 1); row->unlock(); @@ -405,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) { @@ -419,4 +435,17 @@ void TableRow::set_value(TableColumn *col, void *value, size_t vlen) { } } +void TableRow::get_value(TableColumn *col, double *dval) { + memcpy(dval, data + col->index, sizeof(*dval)); +} + +void TableRow::get_value(TableColumn *col, long *lval) { + memcpy(lval, data + col->index, sizeof(*lval)); +} + +void TableRow::get_value(TableColumn *col, char **value, TableStringLength *len) { + memcpy(len, data + col->index, sizeof(*len)); + *value = data + col->index + sizeof(*len); +} + } // namespace swoole diff --git a/src/network/address.cc b/src/network/address.cc index 2175ef69172..4a97dc27793 100644 --- a/src/network/address.cc +++ b/src/network/address.cc @@ -10,15 +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_socket.h" -#include -#include -#include +#include namespace swoole { namespace network { @@ -27,7 +25,9 @@ static thread_local char tmp_address[INET6_ADDRSTRLEN]; const char *Address::get_addr() { if (type == SW_SOCK_TCP || type == SW_SOCK_UDP) { - return inet_ntoa(addr.inet_v4.sin_addr); + if (inet_ntop(AF_INET, &addr.inet_v4.sin_addr, tmp_address, sizeof(tmp_address))) { + return tmp_address; + } } else if (type == SW_SOCK_TCP6 || type == SW_SOCK_UDP6) { if (inet_ntop(AF_INET6, &addr.inet_v6.sin6_addr, tmp_address, sizeof(tmp_address))) { return tmp_address; @@ -48,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) { @@ -76,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 f98f442e47a..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,44 +249,69 @@ 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; } #ifdef SW_USE_OPENSSL +#ifdef SW_SUPPORT_DTLS +void Client::enable_dtls() { + ssl_context->protocols = SW_SSL_DTLS; + socket->dtls = 1; + socket->chunk_size = SW_SSL_BUFFER_SIZE; + send = Client_tcp_send_sync; + recv = Client_tcp_recv_no_buffer; +} +#endif + int Client::enable_ssl_encrypt() { if (ssl_context) { return SW_ERR; } ssl_context.reset(new swoole::SSLContext()); open_ssl = true; +#ifdef SW_SUPPORT_DTLS + if (socket->is_dgram()) { + enable_dtls(); + } +#else + { + swoole_warning("DTLS support require openssl-1.1 or later"); + return SW_ERR; + } +#endif return SW_OK; } int Client::ssl_handshake() { - if (socket->ssl) { + if (socket->ssl_state == SW_SSL_STATE_READY) { return SW_ERR; } - ssl_context->http_v2 = http2; - if (!ssl_context->create()) { - return SW_ERR; - } - socket->ssl_send_ = 1; - if (socket->ssl_create(ssl_context.get(), SW_SSL_CLIENT) < 0) { - return SW_ERR; + if (!ssl_context->ready()) { + ssl_context->http_v2 = http2; + if (!ssl_context->create()) { + return SW_ERR; + } } + if (!socket->ssl) { + socket->ssl_send_ = 1; + if (socket->ssl_create(ssl_context.get(), SW_SSL_CLIENT) < 0) { + return SW_ERR; + } #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (!ssl_context->tls_host_name.empty()) { - SSL_set_tlsext_host_name(socket->ssl, ssl_context->tls_host_name.c_str()); - } + if (!ssl_context->tls_host_name.empty()) { + SSL_set_tlsext_host_name(socket->ssl, ssl_context->tls_host_name.c_str()); + } #endif + } if (socket->ssl_connect() < 0) { return SW_ERR; } @@ -403,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; } @@ -454,7 +471,7 @@ int Client::close() { timer = nullptr; } // onClose callback - if (active && onClose) { + if (active) { active = 0; onClose(this); } @@ -522,13 +539,16 @@ static int Client_tcp_connect_sync(Client *cli, const char *host, int port, doub } #else ret = cli->socket->connect(cli->server_addr); - if (ret < 0 && errno == EINPROGRESS) { - errno = ETIMEDOUT; - } #endif if (ret < 0) { if (errno == EINTR) { continue; + } else if (errno == EINPROGRESS) { + if (nonblock) { + cli->async_connect = true; + } else { + errno = ETIMEDOUT; + } } swoole_set_last_error(errno); } @@ -538,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); @@ -551,23 +570,35 @@ static int Client_tcp_connect_sync(Client *cli, const char *host, int port, doub if (n > 0) { if (cli->socks5_handshake(buf, n) < 0) { return SW_ERR; + } + if (cli->socks5_proxy->state == SW_SOCKS5_STATE_READY) { + break; } else { - if (cli->socks5_proxy->state == SW_SOCKS5_STATE_READY) { - break; - } else { - continue; - } + continue; } } 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 - if (cli->open_ssl) { - if (cli->ssl_handshake() < 0) { - return SW_ERR; - } + if (cli->open_ssl && cli->ssl_handshake() < 0) { + return SW_ERR; } #endif } @@ -584,8 +615,8 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou cli->buffer = new String(cli->input_buffer_size); } - if (!(cli->onConnect && cli->onError && cli->onClose)) { - swWarn("onConnect/onError/onClose callback have not set"); + if (!(cli->onConnect && cli->onError && cli->onClose && cli->onReceive)) { + swoole_warning("onConnect/onError/onReceive/onClose callback have not set"); return SW_ERR; } @@ -599,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; @@ -645,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 { @@ -661,7 +675,7 @@ static int Client_tcp_connect_async(Client *cli, const char *host, int port, dou } static ssize_t Client_tcp_send_async(Client *cli, const char *data, size_t length, int flags) { - int n = length; + ssize_t n = length; if (swoole_event_write(cli->socket, data, length) < 0) { if (swoole_get_last_error() == SW_ERROR_OUTPUT_BUFFER_OVERFLOW) { n = -1; @@ -718,12 +732,8 @@ static int Client_tcp_sendfile_async(Client *cli, const char *filename, off_t of swoole_set_last_error(errno); return SW_ERR; } - if (!(cli->socket->events & SW_EVENT_WRITE)) { - if (cli->socket->events & SW_EVENT_READ) { - return swoole_event_set(cli->socket, SW_EVENT_READ | SW_EVENT_WRITE); - } else { - return swoole_event_add(cli->socket, SW_EVENT_WRITE); - } + if (swoole_event_add_or_update(cli->socket, SW_EVENT_WRITE) == SW_ERR) { + return SW_ERR; } return SW_OK; } @@ -737,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 @@ -756,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; @@ -776,6 +791,11 @@ static int Client_udp_connect(Client *cli, const char *host, int port, double ti return SW_ERR; } + if (cli->async && !cli->onReceive) { + swoole_warning("onReceive callback have not set"); + return SW_ERR; + } + cli->active = 1; cli->timeout = timeout; int bufsize = Socket::default_buffer_size; @@ -791,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; } } @@ -801,15 +821,11 @@ static int Client_udp_connect(Client *cli, const char *host, int port, double ti #ifdef SW_SUPPORT_DTLS { udp_connect = 1; - cli->ssl_context->protocols = SW_SSL_DTLS; - cli->socket->dtls = 1; - cli->socket->chunk_size = SW_SSL_BUFFER_SIZE; - cli->send = Client_tcp_send_sync; - cli->recv = Client_tcp_recv_no_buffer; + cli->enable_dtls(); } #else { - swWarn("DTLS support require openssl-1.1 or later"); + swoole_warning("DTLS support require openssl-1.1 or later"); return SW_ERR; } #endif @@ -878,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; } @@ -931,83 +905,65 @@ 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 { - cli->socket->ssl_state = SW_SSL_STATE_WAIT_STREAM; - return swoole_event_set(event->socket, SW_EVENT_WRITE); - } - if (cli->onConnect) { - execute_onConnect(cli); + 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 - { - if (cli->onConnect) { - execute_onConnect(cli); - } + 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; } if (cli->socket->ssl_state != SW_SSL_STATE_READY) { return SW_OK; - } - // ssl handshake sucess - else if (cli->onConnect) { + } else { execute_onConnect(cli); + return SW_OK; } } #endif @@ -1036,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; @@ -1057,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; @@ -1112,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; @@ -1130,7 +1075,6 @@ static void Client_onResolveCompleted(AsyncEvent *event) { cli->onError(cli); } } - sw_free(event->buf); } static int Client_onWrite(Reactor *reactor, Event *event) { @@ -1167,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; } @@ -1180,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) { @@ -1209,18 +1147,14 @@ static int Client_onWrite(Reactor *reactor, Event *event) { } _connect_success: #endif - if (cli->onConnect) { - execute_onConnect(cli); - } + execute_onConnect(cli); } else { #ifdef SW_USE_OPENSSL _connect_fail: #endif cli->active = 0; cli->close(); - if (cli->onError) { - cli->onError(cli); - } + cli->onError(cli); } return SW_OK; diff --git a/src/network/dns.cc b/src/network/dns.cc index 856419f3515..22ac3ab9dd8 100644 --- a/src/network/dns.cc +++ b/src/network/dns.cc @@ -10,34 +10,133 @@ | to obtain it through the world-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 +#include + +#define SW_PATH_HOSTS "/etc/hosts" + +#ifdef SW_USE_CARES +#include +#endif -#define SW_DNS_SERVER_CONF "/etc/resolv.conf" -#define SW_DNS_SERVER_NUM 2 +using swoole::NameResolver; +using swoole::coroutine::System; -enum swDNS_type { +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) { + swoole_sys_warning("fopen(%s) failed", SwooleG.dns_resolvconf_path.c_str()); + return false; + } + + while (fgets(line, 100, fp)) { + if (strncmp(line, "nameserver", 10) == 0) { + strcpy(buf, strtok(line, " ")); + strcpy(buf, strtok(nullptr, "\n")); + break; + } + } + fclose(fp); + + if (strlen(buf) == 0) { + return false; + } + swoole_set_dns_server(buf); + 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 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 { - SW_DNS_NOT_EXIST, // Error: adress does not exist +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 }; /* Struct for the DNS Header */ -typedef struct { +struct RecordHeader { uint16_t id; uchar rd : 1; uchar tc : 1; @@ -51,72 +150,114 @@ typedef struct { uint16_t ancount; uint16_t nscount; uint16_t arcount; -} swDNSResolver_header; +}; /* Struct for the flags for the DNS Question */ -typedef struct q_flags { +struct Q_FLAGS { uint16_t qtype; uint16_t qclass; -} Q_FLAGS; +}; /* Struct for the flags for the DNS RRs */ -typedef struct rr_flags { +struct RR_FLAGS { uint16_t type; uint16_t rdclass; uint32_t ttl; uint16_t rdlength; -} RR_FLAGS; +}; -static uint16_t swoole_dns_request_id = 1; +static uint16_t dns_request_id = 1; static int domain_encode(const char *src, int n, char *dest); static void domain_decode(char *str); -static int get_dns_server(); - -static int get_dns_server() { - FILE *fp; - char line[100]; - char buf[16] = {}; +static std::string parse_ip_address(void *vaddr, int type); - if ((fp = fopen(SW_DNS_SERVER_CONF, "rt")) == nullptr) { - swSysWarn("fopen(" SW_DNS_SERVER_CONF ") failed"); - return SW_ERR; +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 ""; } - while (fgets(line, 100, fp)) { - if (strncmp(line, "nameserver", 10) == 0) { - strcpy(buf, strtok(line, " ")); - strcpy(buf, strtok(nullptr, "\n")); - break; + 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; } } - fclose(fp); - if (strlen(buf) == 0) { - SwooleG.dns_server_v4 = sw_strdup(SW_DNS_DEFAULT_SERVER); + return ""; +} + +static std::string parse_ip_address(void *vaddr, int type) { + auto addr = reinterpret_cast(vaddr); + std::string ip_addr; + if (type == AF_INET) { + char buff[4 * 4 + 3 + 1]; + sw_snprintf(buff, sizeof(buff), "%u.%u.%u.%u", addr[0], addr[1], addr[2], addr[3]); + return ip_addr.assign(buff); + } else if (type == AF_INET6) { + for (int i = 0; i < 16; i += 2) { + if (i > 0) { + ip_addr.append(":"); + } + char buf[4 + 1]; + size_t n = sw_snprintf(buf, sizeof(buf), "%02x%02x", addr[i], addr[i + 1]); + ip_addr.append(buf, n); + } } else { - SwooleG.dns_server_v4 = sw_strdup(buf); + assert(0); } - - return SW_OK; + return ip_addr; } -std::vector swoole::coroutine::dns_lookup(const char *domain, double timeout) { +std::vector dns_lookup_impl_with_socket(const char *domain, int family, double timeout) { char *_domain_name; Q_FLAGS *qflags = nullptr; char packet[SW_BUFFER_SIZE_STD]; - swDNSResolver_header *header = nullptr; + RecordHeader *header = nullptr; int steps = 0; - std::vector result; - if (SwooleG.dns_server_v4 == nullptr) { - if (get_dns_server() < 0) { - return result; - } + + if (SwooleG.dns_server_host.empty() && !swoole_load_resolv_conf()) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_NO_SERVER); + return result; } - header = (swDNSResolver_header *) packet; - int _request_id = swoole_dns_request_id++; + header = (RecordHeader *) packet; + int _request_id = dns_request_id++; header->id = htons(_request_id); header->qr = 0; header->opcode = 0; @@ -131,20 +272,20 @@ std::vector swoole::coroutine::dns_lookup(const char *domain, doubl header->nscount = 0x0000; header->arcount = 0x0000; - steps = sizeof(swDNSResolver_header); + steps = sizeof(RecordHeader); _domain_name = &packet[steps]; 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; } steps += (strlen((const char *) _domain_name) + 1); qflags = (Q_FLAGS *) &packet[steps]; - qflags->qtype = htons(SW_DNS_A_RECORD); + qflags->qtype = htons(family == AF_INET6 ? SW_DNS_AAAA_RECORD : SW_DNS_A_RECORD); qflags->qclass = htons(0x0001); steps += sizeof(Q_FLAGS); @@ -152,16 +293,8 @@ std::vector swoole::coroutine::dns_lookup(const char *domain, doubl if (timeout > 0) { _sock.set_timeout(timeout); } - - char *_port; - int dns_server_port = SW_DNS_SERVER_PORT; - char dns_server_host[32]; - strcpy(dns_server_host, SwooleG.dns_server_v4); - if ((_port = strchr(SwooleG.dns_server_v4, ':'))) { - dns_server_port = atoi(_port + 1); - dns_server_host[_port - SwooleG.dns_server_v4] = '\0'; - } - if (!_sock.sendto(dns_server_host, dns_server_port, (char *) packet, steps)) { + if (!_sock.sendto(SwooleG.dns_server_host, SwooleG.dns_server_port, (char *) packet, steps)) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); return result; } @@ -182,14 +315,15 @@ std::vector swoole::coroutine::dns_lookup(const char *domain, doubl char name[10][254]; int i, j; - int ret = _sock.recv(packet, sizeof(packet) - 1); + 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); return result; } packet[ret] = 0; - header = (swDNSResolver_header *) packet; - steps = sizeof(swDNSResolver_header); + header = (RecordHeader *) packet; + steps = sizeof(RecordHeader); _domain_name = &packet[steps]; domain_decode(_domain_name); @@ -210,7 +344,7 @@ std::vector swoole::coroutine::dns_lookup(const char *domain, doubl temp = &packet[steps]; j = 0; while (*temp != 0) { - if ((uchar)(*temp) == 0xc0) { + if ((uchar) (*temp) == 0xc0) { ++temp; temp = &packet[(uint8_t) *temp]; } else { @@ -229,11 +363,9 @@ std::vector swoole::coroutine::dns_lookup(const char *domain, doubl steps = steps + sizeof(RR_FLAGS) - 2; /* Parsing the IPv4 address in the RR */ - if (ntohs(rrflags->type) == 1) { - for (j = 0; j < ntohs(rrflags->rdlength); ++j) { - rdata[i][j] = (uchar) packet[steps + j]; - } - type[i] = ntohs(rrflags->type); + type[i] = ntohs(rrflags->type); + for (j = 0; j < ntohs(rrflags->rdlength); ++j) { + rdata[i][j] = (uchar) packet[steps + j]; } /* Parsing the canonical name in the RR */ @@ -241,7 +373,7 @@ std::vector swoole::coroutine::dns_lookup(const char *domain, doubl temp = &packet[steps]; j = 0; while (*temp != 0) { - if ((uchar)(*temp) == 0xc0) { + if ((uchar) (*temp) == 0xc0) { ++temp; temp = &packet[(uint8_t) *temp]; } else { @@ -260,16 +392,17 @@ std::vector swoole::coroutine::dns_lookup(const char *domain, doubl int request_id = ntohs(header->id); // bad response if (request_id != _request_id) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); return result; } for (i = 0; i < ancount; i++) { - if (type[i] != SW_DNS_A_RECORD) { + if (type[i] != SW_DNS_A_RECORD && type[i] != SW_DNS_AAAA_RECORD) { continue; } - char address[16]; - size_t n = - sw_snprintf(address, sizeof(address), "%d.%d.%d.%d", rdata[i][0], rdata[i][1], rdata[i][2], rdata[i][3]); - result.push_back(std::string(address, n)); + result.push_back(parse_ip_address(rdata[i], type[i] == SW_DNS_A_RECORD ? AF_INET : AF_INET6)); + } + if (result.empty()) { + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); } return result; } @@ -320,16 +453,218 @@ static void domain_decode(char *str) { str[i - 1] = '\0'; } -namespace swoole { +#ifdef SW_USE_CARES +struct ResolvContext { + ares_channel channel; + ares_options ares_opts; + int ares_flags; + int error; + bool completed; + Coroutine *co; + std::shared_ptr defer_task_cancelled; + std::unordered_map sockets; + std::vector result; +}; + +std::vector dns_lookup_impl_with_cares(const char *domain, int family, double timeout) { + if (!swoole_event_isset_handler(SW_FD_CARES)) { + 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); + 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); + 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; + }); + sw_reactor()->add_destroy_callback([](void *_data) { ares_library_cleanup(); }, nullptr); + } + + ResolvContext ctx{}; + 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; + ctx.ares_opts.timeout = timeout * 1000; + ctx.ares_opts.tries = SwooleG.dns_tries; + ctx.ares_opts.sock_state_cb_data = &ctx; + ctx.ares_opts.sock_state_cb = [](void *arg, int fd, int readable, int writable) { + auto ctx = reinterpret_cast(arg); + int events = 0; + if (readable) { + events |= SW_EVENT_READ; + } + if (writable) { + events |= SW_EVENT_WRITE; + } + + 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) { + swoole_warning("error events, fd=%d", fd); + return; + } + _socket = make_socket(fd, SW_FD_CARES); + _socket->object = ctx; + ctx->sockets[fd] = _socket; + } else { + _socket = ctx->sockets[fd]; + if (events == 0) { + swoole_trace_log(SW_TRACE_CARES, "[del event], fd=%d", fd); + swoole_event_del(_socket); + _socket->fd = -1; + _socket->free(); + ctx->sockets.erase(fd); + return; + } + } + + if (_socket->events) { + swoole_event_set(_socket, events); + swoole_trace_log(SW_TRACE_CARES, "[set event] fd=%d, events=%d", fd, events); + } else { + swoole_event_add(_socket, 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) { + swoole_warning("ares_init_options() failed, Error: %s[%d]", ares_strerror(res), res); + goto _return; + } + + if (!SwooleG.dns_server_host.empty()) { +#if (ARES_VERSION >= 0x010b00) + struct ares_addr_port_node servers; + servers.family = AF_INET; + servers.next = nullptr; + inet_pton(AF_INET, SwooleG.dns_server_host.c_str(), &servers.addr.addr4); + servers.tcp_port = 0; + servers.udp_port = SwooleG.dns_server_port; + ares_set_servers_ports(ctx.channel, &servers); +#elif (ARES_VERSION >= 0x010701) + struct ares_addr_node servers; + servers.family = AF_INET; + servers.next = nullptr; + 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) { + swoole_warning("not support to set port of dns server"); + } +#else + swoole_warning("not support to set dns server"); +#endif + } + + ares_gethostbyname( + ctx.channel, + domain, + family, + [](void *data, int status, int timeouts, struct hostent *hostent) { + auto ctx = reinterpret_cast(data); + + swoole_trace_log(SW_TRACE_CARES, "[cares callback] status=%d, timeouts=%d", status, timeouts); + + if (timeouts > 0) { + ctx->error = SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT; + goto _resume; + } + + if (status != ARES_SUCCESS) { + ctx->error = status; + goto _resume; + } + + if (hostent->h_addr_list) { + char **paddr = hostent->h_addr_list; + while (*paddr != nullptr) { + ctx->result.emplace_back(parse_ip_address(*paddr, hostent->h_addrtype)); + paddr++; + } + } + _resume: + if (ctx->co && ctx->co->is_suspending()) { + auto _cancelled = ctx->defer_task_cancelled; + swoole_event_defer( + [_cancelled](void *data) { + if (*_cancelled) { + return; + } + Coroutine *co = reinterpret_cast(data); + co->resume(); + }, + ctx->co); + ctx->co = nullptr; + } else { + ctx->completed = true; + } + }, + &ctx); + + if (ctx.error || ctx.completed) { + goto _destroy; + } + + co->yield_ex(timeout); + if (co->is_canceled()) { + ares_cancel(ctx.channel); + } else if (co->is_timedout()) { + ares_process_fd(ctx.channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); + ctx.error = ARES_ETIMEOUT; + } else { + swoole_trace_log(SW_TRACE_CARES, "lookup success, result_count=%lu", ctx.result.size()); + } +_destroy: + if (ctx.error) { + switch (ctx.error) { + case ARES_ECANCELLED: + swoole_set_last_error(SW_ERROR_CO_CANCELED); + break; + case ARES_ETIMEOUT: + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_TIMEOUT); + break; + default: + swoole_set_last_error(SW_ERROR_DNSLOOKUP_RESOLVE_FAILED); + break; + } + } + *ctx.defer_task_cancelled = true; + ares_destroy(ctx.channel); +_return: + return ctx.result; +} +#endif + +std::vector dns_lookup(const char *domain, int family, double timeout) { +#ifdef SW_USE_CARES + return dns_lookup_impl_with_cares(domain, family, timeout); +#else + return dns_lookup_impl_with_socket(domain, family, timeout); +#endif +} + +} // namespace coroutine + +/** + * blocking-IO, Use in synchronous mode or AIO thread pool + */ namespace network { #ifndef HAVE_GETHOSTBYNAME2_R +#include static std::mutex g_gethostbyname2_lock; #endif -/** - * DNS lookup - */ #ifdef HAVE_GETHOSTBYNAME2_R int gethostbyname(int flags, const char *name, char *addr) { int __af = flags & (~SW_DNS_LOOKUP_RANDOM); @@ -363,7 +698,7 @@ int gethostbyname(int flags, const char *name, char *addr) { union { char v4[INET_ADDRSTRLEN]; char v6[INET6_ADDRSTRLEN]; - } addr_list[SW_DNS_HOST_BUFFER_SIZE]; + } addr_list[SW_DNS_HOST_BUFFER_SIZE]{}; int i = 0; for (i = 0; i < SW_DNS_HOST_BUFFER_SIZE; i++) { @@ -426,42 +761,46 @@ int gethostbyname(int flags, const char *name, char *addr) { int getaddrinfo(GetaddrinfoRequest *req) { struct addrinfo *result = nullptr; struct addrinfo *ptr = nullptr; - struct addrinfo hints{}; + struct addrinfo hints {}; hints.ai_family = req->family; 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; @@ -470,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) { @@ -483,6 +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 6e10e9c7ec2..791195441dc 100644 --- a/src/network/socket.cc +++ b/src/network/socket.cc @@ -10,13 +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_socket.h" -#include #include #include "swoole_api.h" @@ -74,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; @@ -90,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; } @@ -104,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; @@ -128,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 { @@ -174,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; } @@ -200,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; } } @@ -229,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; @@ -277,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; @@ -297,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; @@ -309,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; } @@ -321,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); @@ -336,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; } @@ -356,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); @@ -375,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); @@ -412,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; @@ -420,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; @@ -430,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; @@ -437,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; @@ -453,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) { @@ -467,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; } } @@ -479,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) { @@ -493,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; } } @@ -534,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(); @@ -552,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: @@ -597,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; @@ -629,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; } @@ -669,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; @@ -711,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; } @@ -741,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; } @@ -754,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; @@ -776,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); @@ -805,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; } @@ -817,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); @@ -856,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; @@ -885,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; } @@ -921,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: @@ -973,61 +1010,92 @@ X509 *Socket::ssl_get_peer_certificate() { return SSL_get_peer_certificate(ssl); } -bool Socket::ssl_get_peer_certificate(String *buf) { - int n = ssl_get_peer_certificate(buf->str, buf->size); - if (n < 0) { - return false; - } else { - buf->length = n; - return true; +STACK_OF(X509) * Socket::ssl_get_peer_cert_chain() { + if (!ssl) { + return NULL; } + return SSL_get_peer_cert_chain(ssl); } -int Socket::ssl_get_peer_certificate(char *buffer, size_t length) { +static int _ssl_read_x509_file(X509 *cert, char *buffer, size_t length) { long len; - BIO *bio; - X509 *cert; - int n; - - cert = ssl_get_peer_certificate(); - if (cert == nullptr) { - return SW_ERR; - } + BIO *bio = BIO_new(BIO_s_mem()); + ON_SCOPE_EXIT { + BIO_free(bio); + }; - bio = BIO_new(BIO_s_mem()); if (bio == nullptr) { - swWarn("BIO_new() failed"); - X509_free(cert); - return SW_ERR; + swoole_warning("BIO_new() failed"); + return -1; } if (PEM_write_bio_X509(bio, cert) == 0) { - swWarn("PEM_write_bio_X509() failed"); - goto _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); - goto _failed; + swoole_warning("certificate length[%ld] is too big", len); + return -1; } + return BIO_read(bio, buffer, len); +} - n = BIO_read(bio, buffer, len); +std::vector Socket::ssl_get_peer_cert_chain(int limit) { + std::vector list; + STACK_OF(X509) *chain = ssl_get_peer_cert_chain(); + if (chain == nullptr) { + return list; + } + auto n = sk_X509_num(chain); - BIO_free(bio); - X509_free(cert); +#ifdef OPENSSL_IS_BORINGSSL + n = std::min((int) n, limit); +#else + n = std::min(n, limit); +#endif - return n; + 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); + if (n > 0) { + list.emplace_back(sw_tg_buffer()->str, n); + } + } + return list; +} -_failed: +bool Socket::ssl_get_peer_certificate(String *buf) { + int n = ssl_get_peer_certificate(buf->str, buf->size); + if (n < 0) { + return false; + } else { + buf->length = n; + return true; + } +} - BIO_free(bio); - X509_free(cert); +int Socket::ssl_get_peer_certificate(char *buffer, size_t length) { + X509 *cert = ssl_get_peer_certificate(); + if (cert == nullptr) { + return SW_ERR; + } + ON_SCOPE_EXIT { + if (cert) { + X509_free(cert); + } + }; + return _ssl_read_x509_file(cert, buffer, length); +} - return SW_ERR; +const char *Socket::ssl_get_error_reason(int *reason) { + int error = ERR_get_error(); + *reason = ERR_GET_REASON(error); + return ERR_reason_error_string(error); } -enum swReturn_code Socket::ssl_accept() { +ReturnCode Socket::ssl_accept() { ssl_clear_error(); int n = SSL_accept(ssl); @@ -1062,10 +1130,9 @@ enum swReturn_code Socket::ssl_accept() { ssl_want_write = 1; return SW_WAIT; } else if (err == SSL_ERROR_SSL) { - int error = ERR_get_error(); - int reason = ERR_GET_REASON(error); - const char *error_string = ERR_reason_error_string(error); - swWarn( + int reason; + const char *error_string = ssl_get_error_reason(&reason); + 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) { @@ -1077,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; } @@ -1091,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; @@ -1109,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) { @@ -1120,12 +1187,18 @@ int Socket::ssl_connect() { long err_code = ERR_get_error(); char *msg = ERR_error_string(err_code, sw_tg_buffer()->str); - swWarn("SSL_connect(fd=%d) failed. Error: %s[%ld|%d]", fd, 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; @@ -1133,34 +1206,27 @@ 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; } } -void Socket::ssl_close() { - int n, sslerr, err; - +bool Socket::ssl_shutdown() { + if (ssl_closed_) { + return false; + } if (SSL_in_init(ssl)) { - /* - * OpenSSL 1.0.2f complains if SSL_shutdown() is called during - * an SSL handshake, while previous versions always return 0. - * Avoid calling SSL_shutdown() if handshake wasn't completed. - */ - SSL_free(ssl); - ssl = nullptr; - return; + return false; } - /** * If the peer close first, local should be set to quiet mode and do not send any data, * otherwise the peer will send RST segment. @@ -1170,26 +1236,38 @@ void Socket::ssl_close() { } int mode = SSL_get_shutdown(ssl); - SSL_set_shutdown(ssl, mode | SSL_RECEIVED_SHUTDOWN | SSL_SENT_SHUTDOWN); - n = SSL_shutdown(ssl); - - swTrace("SSL_shutdown: %d", n); - - sslerr = 0; + int n = SSL_shutdown(ssl); + ssl_closed_ = 1; + 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)) { - err = (sslerr == SSL_ERROR_SYSCALL) ? errno : 0; - swWarn("SSL_shutdown() failed. Error: %d:%d", sslerr, err); + int reason; + const char *error_string = ssl_get_error_reason(&reason); + swoole_warning("SSL_shutdown() failed, reason=%d, error_string=%s", reason, error_string); + return false; } + return true; +} + +void Socket::ssl_close() { + /* + * OpenSSL 1.0.2f complains if SSL_shutdown() is called during + * an SSL handshake, while previous versions always return 0. + * Avoid calling SSL_shutdown() if handshake wasn't completed. + */ + if (!ssl_closed_) { + ssl_shutdown(); + } SSL_free(ssl); ssl = nullptr; } @@ -1291,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(); @@ -1374,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) { @@ -1387,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; } @@ -1401,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; @@ -1422,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) { @@ -1456,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 9c287467111..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 | +----------------------------------------------------------------------+ */ @@ -18,7 +18,6 @@ #include "swoole_api.h" #include "swoole_string.h" #include "swoole_socket.h" -#include "swoole_reactor.h" #include "swoole_protocol.h" #include "swoole_client.h" @@ -75,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; } @@ -90,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; @@ -107,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; } @@ -118,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) { @@ -128,26 +129,17 @@ int Stream::send(const char *data, size_t length) { return SW_OK; } -int Stream::recv_blocking(Socket *sock, void *__buf, size_t __len) { +ssize_t Stream::recv_blocking(Socket *sock, void *__buf, size_t __len) { int tmp = 0; ssize_t ret = sock->recv_blocking(&tmp, sizeof(tmp), MSG_WAITALL); - if (ret <= 0) { - return SW_CLOSE; + return SW_ERR; } int length = (int) ntohl(tmp); - if (length <= 0) { - return SW_CLOSE; - } else if (length > (int) __len) { - return SW_CLOSE; - } - - ret = sock->recv_blocking(__buf, length, MSG_WAITALL); - if (ret <= 0) { - return SW_CLOSE; - } else { - return SW_READY; + if (length <= 0 || length > (int) __len) { + return SW_ERR; } + return sock->recv_blocking(__buf, length, MSG_WAITALL); } } // namespace network diff --git a/src/os/async_thread.cc b/src/os/async_thread.cc index db1035b7b3c..63f7dc90f51 100644 --- a/src/os/async_thread.cc +++ b/src/os/async_thread.cc @@ -10,7 +10,7 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ @@ -31,22 +31,14 @@ #include #include #include +#include + +static std::mutex async_thread_lock; +static std::shared_ptr async_thread_pool; namespace swoole { namespace async { //------------------------------------------------------------------------------- -static std::mutex init_lock; -static std::atomic refcount(0); -static void aio_thread_release(AsyncEvent *event); -static thread_local std::string tmp_thread_id; - -static const char *get_thread_id(std::thread::id id) { - std::stringstream ss; - ss << id; - tmp_thread_id = ss.str(); - return tmp_thread_id.c_str(); -} - class EventQueue { public: inline void push(AsyncEvent *event) { @@ -88,14 +80,16 @@ class ThreadPool { worker_num = _worker_num == 0 ? SW_CPU_NUM * SW_AIO_THREAD_NUM_MULTIPLE : SW_MAX(core_worker_num, _worker_num); max_wait_time = _max_wait_time == 0 ? SW_AIO_TASK_MAX_WAIT_TIME : _max_wait_time; max_idle_time = _max_idle_time == 0 ? SW_AIO_THREAD_MAX_IDLE_TIME : _max_idle_time; - - creator_pid = getpid(); } ~ThreadPool() { shutdown(); } + bool is_running() { + return running; + } + bool start() { running = true; current_task_id = 0; @@ -124,16 +118,14 @@ class ThreadPool { } delete _thread; } + threads.clear(); return true; } void schedule() { if (n_waiting == 0 && threads.size() < worker_num && max_wait_time > 0) { - event_mutex.lock(); double _max_wait_time = _queue.get_max_wait_time(); - event_mutex.unlock(); - if (_max_wait_time > max_wait_time) { size_t n = 1; /** @@ -142,11 +134,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(); } @@ -155,42 +147,45 @@ class ThreadPool { } AsyncEvent *dispatch(const AsyncEvent *request) { - if (SwooleTG.aio_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.aio_write_socket; - event_mutex.lock(); + _event_copy->pipe_socket = SwooleTG.async_threads->write_socket; _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(); } - pid_t get_creator_pid() { - return creator_pid; + static std::string get_thread_id(std::thread::id id) { + std::stringstream ss; + ss << id; + return ss.str(); } 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)); + 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), 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(); } @@ -199,14 +194,21 @@ class ThreadPool { } } -#ifdef SW_DEBUG + static void release_callback(AsyncEvent *event) { + std::thread::id *tid = reinterpret_cast(event->object); + SwooleTG.async_threads->pool->release_thread(*tid); + delete tid; + // balance + SwooleTG.async_threads->task_num++; + } + void notify_one() { _cv.notify_one(); } -#endif 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; @@ -218,219 +220,131 @@ class ThreadPool { std::atomic n_waiting; std::atomic n_closing; size_t current_task_id = 0; - pid_t creator_pid; std::unordered_map threads; EventQueue _queue; std::mutex event_mutex; std::condition_variable _cv; }; -static ThreadPool *pool = nullptr; +void ThreadPool::main_func(bool is_core_worker) { + bool exit_flag = false; + swoole_thread_init(); -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->ret = -1; - } else if (sw_unlikely(event->canceled)) { - event->error = SW_ERROR_AIO_CANCELED; - event->ret = -1; - } else { - event->handler(event); - } + while (running) { + event_mutex.lock(); + AsyncEvent *event = _queue.pop(); + event_mutex.unlock(); - swTraceLog(SW_TRACE_AIO, - "aio_thread %s. ret=%d, error=%d", - event->ret > 0 ? "ok" : "failed", - event->ret, - 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; - } + 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); + } - // exit - if (exit_flag) { - n_closing--; - break; - } - } else { - std::unique_lock lock(event_mutex); - if (_queue.count() > 0) { + 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 = aio_thread_release; - 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"); } - --n_waiting; } + break; } - }); - 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"); - return; - } -} - -static void aio_thread_release(AsyncEvent *event) { - std::thread::id *tid = reinterpret_cast(event->object); - pool->release_thread(*tid); - delete tid; - // balance - SwooleTG.aio_task_num++; -} -static void destroy(void *private_data) { - if (!SwooleTG.aio_init) { - return; - } - SwooleTG.aio_init = 0; - swoole_event_del(SwooleTG.aio_read_socket); - - if (pool->get_creator_pid() == getpid()) { - if ((--refcount) == 0) { - delete pool; - pool = nullptr; - - SwooleTG.aio_pipe->close(); - SwooleTG.aio_read_socket = nullptr; - SwooleTG.aio_write_socket = nullptr; - delete SwooleTG.aio_pipe; - SwooleTG.aio_pipe = nullptr; + // 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; + } + break; + } + } + --n_waiting; } } + swoole_thread_clean(); } -static int init() { - if (SwooleTG.aio_init) { - swWarn("aio_thread_pool has already been initialized"); - return SW_ERR; - } - if (!SwooleTG.reactor) { - swWarn("no event loop, cannot initialized"); - return SW_ERR; - } - - SwooleTG.aio_pipe = new Pipe(false); - - if (!SwooleTG.aio_pipe->ready()) { - delete SwooleTG.aio_pipe; - SwooleTG.aio_pipe = nullptr; - swoole_throw_error(SW_ERROR_SYSTEM_CALL_FAIL); - } - - SwooleTG.aio_read_socket = SwooleTG.aio_pipe->get_socket(false); - SwooleTG.aio_write_socket = SwooleTG.aio_pipe->get_socket(true); - SwooleTG.aio_read_socket->fd_type = SW_FD_AIO; - SwooleTG.aio_write_socket->fd_type = SW_FD_AIO; - - swoole_event_add(SwooleTG.aio_read_socket, SW_EVENT_READ); - - sw_reactor()->add_destroy_callback(destroy); - sw_reactor()->set_exit_condition(Reactor::EXIT_CONDITION_AIO_TASK, - [](Reactor *reactor, int &event_num) -> bool { - if (SwooleTG.aio_init && SwooleTG.aio_task_num == 0) { - event_num--; - } - return true; - }); - - init_lock.lock(); - if ((refcount++) == 0) { - pool = new ThreadPool( - SwooleG.aio_core_worker_num, SwooleG.aio_worker_num, SwooleG.aio_max_wait_time, SwooleG.aio_max_idle_time); - pool->start(); - SwooleTG.aio_schedule = 1; - SwooleG.aio_default_socket = SwooleTG.aio_write_socket; +void ThreadPool::create_thread(const bool is_core_worker) { + try { + std::thread *_thread = new std::thread([this, is_core_worker]() { main_func(is_core_worker); }); + threads[_thread->get_id()] = _thread; + } catch (const std::system_error &e) { + swoole_sys_notice("create aio thread failed, please check your system configuration or adjust aio_worker_num"); + return; } - SwooleTG.aio_init = 1; - init_lock.unlock(); - - return SW_OK; -} - -size_t thread_count() { - return pool ? pool->worker_count() : 0; } AsyncEvent *dispatch(const AsyncEvent *request) { - if (sw_unlikely(!SwooleTG.aio_init)) { - init(); + if (sw_unlikely(!SwooleTG.async_threads)) { + SwooleTG.async_threads = new AsyncThreads(); } - AsyncEvent *event = pool->dispatch(request); + AsyncEvent *event = SwooleTG.async_threads->pool->dispatch(request); if (sw_likely(event)) { - SwooleTG.aio_task_num++; + SwooleTG.async_threads->task_num++; } return event; } -int callback(Reactor *reactor, Event *event) { - if (SwooleTG.aio_schedule) { - pool->schedule(); - } +//------------------------------------------------------------------------------- +} // namespace async +int AsyncThreads::callback(Reactor *reactor, Event *event) { 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++) { @@ -438,21 +352,93 @@ int callback(Reactor *reactor, Event *event) { if (!event->canceled) { event->callback(event); } - SwooleTG.aio_task_num--; + SwooleTG.async_threads->task_num--; delete event; } return SW_OK; } -#ifdef SW_DEBUG -void notify_one() { +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() { if (pool) { pool->notify_one(); } } -#endif -//------------------------------------------------------------------------------- -} // namespace async +AsyncThreads::AsyncThreads() { + if (!SwooleTG.reactor) { + swoole_warning("no event loop, cannot initialized"); + throw swoole::Exception(SW_ERROR_WRONG_OPERATION); + } + + pipe = new Pipe(false); + if (!pipe->ready()) { + delete pipe; + pipe = nullptr; + swoole_throw_error(SW_ERROR_SYSTEM_CALL_FAIL); + } + + read_socket = pipe->get_socket(false); + write_socket = pipe->get_socket(true); + read_socket->fd_type = SW_FD_AIO; + write_socket->fd_type = SW_FD_AIO; + + swoole_event_add(read_socket, SW_EVENT_READ); + + sw_reactor()->add_destroy_callback([](void *data) { + if (!SwooleTG.async_threads) { + return; + } + swoole_event_del(SwooleTG.async_threads->read_socket); + delete SwooleTG.async_threads; + SwooleTG.async_threads = nullptr; + }); + + 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; + }); + + 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() { + 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; + delete pipe; + pipe = nullptr; +} }; // namespace swoole diff --git a/src/os/base.cc b/src/os/base.cc index 662285446ce..94e8e7ad64f 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -12,39 +12,35 @@ | to obtain it through the world-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_string.h" #include "swoole_socket.h" #include "swoole_async.h" -#include -#include #include -#include #if __APPLE__ 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; } @@ -53,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; @@ -82,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 = swoole::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,12 +108,12 @@ void handler_gethostbyname(AsyncEvent *event) { ret = 0; } } - event->ret = ret; + event->retval = ret; } void handler_getaddrinfo(AsyncEvent *event) { - swoole::network::GetaddrinfoRequest *req = (swoole::network::GetaddrinfoRequest *) event->req; - event->ret = swoole::network::getaddrinfo(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 6fd4074c085..89d98448a07 100644 --- a/src/os/msg_queue.cc +++ b/src/os/msg_queue.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.h" #include "swoole_msg_queue.h" -#include #include #include @@ -25,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; @@ -50,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); } @@ -68,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; @@ -83,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; } @@ -96,7 +94,11 @@ bool MsgQueue::stat(size_t *queue_num, size_t *queue_bytes) { struct msqid_ds __stat; if (msgctl(msg_id_, IPC_STAT, &__stat) == 0) { *queue_num = __stat.msg_qnum; +#ifndef __NetBSD__ *queue_bytes = __stat.msg_cbytes; +#else + *queue_bytes = __stat._msg_cbytes; +#endif return true; } else { return false; @@ -110,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 c550c107bef..4a7dbf53499 100644 --- a/src/os/pipe.cc +++ b/src/os/pipe.cc @@ -10,15 +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_pipe.h" #include "swoole_socket.h" -#include - namespace swoole { using network::Socket; @@ -42,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])) { @@ -61,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; } @@ -91,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 52b24b64a9f..634be9ac1ac 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -10,36 +10,40 @@ | to obtain it through the world-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_memory.h" -#include "swoole_signal.h" #include "swoole_socket.h" #include "swoole_string.h" #include "swoole_msg_queue.h" #include "swoole_pipe.h" #include "swoole_server.h" +#include "swoole_util.h" #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; + } +} -static void ProcessPool_kill_timeout_worker(Timer *timer, TimerNode *tnode) { +void ProcessPool::kill_timeout_worker(Timer *timer, TimerNode *tnode) { uint32_t i; pid_t reload_worker_pid = 0; ProcessPool *pool = (ProcessPool *) tnode->data; @@ -52,9 +56,10 @@ static void ProcessPool_kill_timeout_worker(Timer *timer, TimerNode *tnode) { continue; } if (swoole_kill(reload_worker_pid, SIGKILL) < 0) { - swSysWarn("swKill(%d, SIGKILL) [%d] failed", 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("swKill(%d, SIGKILL) [%d]", pool->reload_workers[i].pid, i); + swoole_warning("force kill worker process(pid=%d, id=%d)", pool->reload_workers[i].pid, i); } } } @@ -62,76 +67,126 @@ static void ProcessPool_kill_timeout_worker(Timer *timer, TimerNode *tnode) { pool->reload_worker_i = 0; pool->reload_init = false; } + /** * Process manager */ -int ProcessPool::create(ProcessPool *pool, uint32_t worker_num, key_t msgqueue_key, int ipc_mode) { - *pool = {}; - pool->worker_num = worker_num; - +int ProcessPool::create(uint32_t _worker_num, key_t _msgqueue_key, swIPCMode _ipc_mode) { + worker_num = _worker_num; /** * Shared memory is used here */ - pool->workers = (Worker *) sw_mem_pool()->alloc(worker_num * sizeof(Worker)); - if (pool->workers == nullptr) { - swSysWarn("malloc[1] failed"); + workers = (Worker *) sw_mem_pool()->alloc(_worker_num * sizeof(Worker)); + if (workers == nullptr) { + swoole_sys_warning("malloc[1] failed"); return SW_ERR; } - if (ipc_mode == SW_IPC_MSGQUEUE) { - pool->use_msgqueue = 1; - pool->msgqueue_key = msgqueue_key; - pool->queue = new MsgQueue(pool->msgqueue_key); - if (!pool->queue->ready()) { - delete pool->queue; - pool->queue = nullptr; + if (create_message_box(SW_MESSAGE_BOX_SIZE) < 0) { + return SW_ERR; + } + + if (_ipc_mode == SW_IPC_MSGQUEUE) { + use_msgqueue = 1; + msgqueue_key = _msgqueue_key; + queue = new MsgQueue(msgqueue_key); + if (!queue->ready()) { + delete queue; + queue = nullptr; return SW_ERR; } - } else if (ipc_mode == SW_IPC_UNIXSOCK) { - pool->pipes = new std::vector>; - SW_LOOP_N(worker_num) { + } else if (_ipc_mode == SW_IPC_UNIXSOCK) { + pipes = new std::vector>; + SW_LOOP_N(_worker_num) { auto sock = new UnixSocket(true, SOCK_DGRAM); if (!sock->ready()) { delete sock; - delete pool->pipes; - pool->pipes = nullptr; + delete pipes; + pipes = nullptr; return SW_ERR; } - pool->pipes->emplace_back(sock); - pool->workers[i].pipe_master = sock->get_socket(true); - pool->workers[i].pipe_worker = sock->get_socket(false); - pool->workers[i].pipe_object = sock; - } - } else if (ipc_mode == SW_IPC_SOCKET) { - pool->use_socket = 1; - pool->stream_info_ = new StreamInfo(); + 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; + } + } else if (_ipc_mode == SW_IPC_SOCKET) { + use_socket = 1; + stream_info_ = new StreamInfo(); } else { - ipc_mode = SW_IPC_NONE; + _ipc_mode = SW_IPC_NONE; } - pool->map_ = new std::unordered_map; + map_ = new std::unordered_map; + ipc_mode = _ipc_mode; + 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; - pool->ipc_mode = ipc_mode; - if (ipc_mode > SW_IPC_NONE) { - pool->main_loop = ProcessPool_worker_loop; + SW_LOOP_N(_worker_num) { + workers[i].pool = this; } - SW_LOOP_N(worker_num) { - pool->workers[i].pool = pool; + 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::create_unix_socket(const char *socket_file, int blacklog) { +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); if (stream_info_->socket_file == nullptr) { return SW_ERR; } + stream_info_->socket_port = 0; stream_info_->socket = make_server_socket(SW_SOCK_UNIX_STREAM, stream_info_->socket_file, 0, blacklog); if (!stream_info_->socket) { return SW_ERR; @@ -139,15 +194,16 @@ int ProcessPool::create_unix_socket(const char *socket_file, int blacklog) { return SW_OK; } -int ProcessPool::create_tcp_socket(const char *host, int port, 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); if (stream_info_->socket_file == nullptr) { return SW_ERR; } + stream_info_->socket_port = port; stream_info_->socket = make_server_socket(SW_SOCK_TCP, host, port, blacklog); if (!stream_info_->socket) { return SW_ERR; @@ -155,25 +211,67 @@ int ProcessPool::create_tcp_socket(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; } @@ -183,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; } @@ -192,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; } @@ -212,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) { @@ -225,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; @@ -240,38 +365,38 @@ 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; } -/** - * dispatch data to worker - */ -int ProcessPool::dispatch_blocking(swEventData *data, int *dst_worker_id) { - int ret = 0; - int sendn = sizeof(data->info) + data->info.len; +swResultCode ProcessPool::dispatch_blocking(const char *data, uint32_t len) { + assert(use_socket); + network::Client _socket(stream_info_->socket->socket_type, false); + if (!_socket.socket) { + return SW_ERR; + } + if (_socket.connect(&_socket, stream_info_->socket_file, stream_info_->socket_port, -1, 0) < 0) { + return SW_ERR; + } + uint32_t packed_len = htonl(len); + if (_socket.send(&_socket, (char *) &packed_len, 4, 0) < 0) { + return SW_ERR; + } + if (_socket.send(&_socket, (char *) data, len, 0) < 0) { + return SW_ERR; + } + _socket.close(); + return SW_OK; +} + +swResultCode ProcessPool::dispatch_blocking(EventData *data, int *dst_worker_id) { if (use_socket) { - swoole::network::Client _socket(SW_SOCK_UNIX_STREAM, false); - if (!_socket.socket) { - return SW_ERR; - } - if (_socket.connect(&_socket, stream_info_->socket_file, 0, -1, 0) < 0) { - return SW_ERR; - } - if (_socket.send(&_socket, (char *) data, sendn, 0) < 0) { - return SW_ERR; - } - _socket.close(); - return SW_OK; + return dispatch_blocking((char *) data, data->size()); } if (*dst_worker_id < 0) { @@ -281,14 +406,49 @@ int ProcessPool::dispatch_blocking(swEventData *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); + } + + if (onWorkerExit) { + reactor->set_end_callback(Reactor::PRIORITY_TRY_EXIT, [this, worker](Reactor *reactor) { + onWorkerExit(this, worker); + if (reactor->if_exit()) { + reactor->running = false; + } + }); } +} - return ret; +void ProcessPool::kill_all_workers(int signo) { + SW_LOOP_N(worker_num) { + swoole_kill(workers[i].pid, signo); + } } void ProcessPool::shutdown() { @@ -297,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; @@ -321,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: @@ -358,108 +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; - swEventData buf; + 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 == SW_CLOSE) { + 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)); @@ -467,69 +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; - } - - if (ret >= 0 && !worker_task_always) { - task_n--; + _end: + if (sw_timer()) { + sw_timer()->select(); } } + + SwooleTG.timer_scheduler = nullptr; + return SW_OK; } -int 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); - } - max_packet_size_ = max_packet_size; - main_loop = ProcessPool_worker_loop_ex; +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; +} +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; } -static int ProcessPool_worker_loop_ex(ProcessPool *pool, Worker *worker) { +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); + } + } + return swoole_event_wait(); +} + +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_) { @@ -538,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; @@ -576,58 +778,145 @@ 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; } -int ProcessPool::wait() { - int pid, new_pid; - pid_t reload_worker_pid = 0; - int ret; - int status; +void ProcessPool::add_worker(Worker *worker) { + map_->emplace(std::make_pair(worker->pid, worker)); +} - reload_workers = (Worker *) sw_calloc(worker_num, sizeof(Worker)); - if (reload_workers == nullptr) { - swError("malloc[reload_workers] failed"); - return SW_ERR; +bool ProcessPool::detach() { + if (!running || !message_box) { + return false; } + WorkerStopMessage msg; + msg.pid = getpid(); + msg.worker_id = swoole_get_process_id(); + + if (push_message(SW_WORKER_MESSAGE_STOP, &msg, sizeof(msg)) < 0) { + return false; + } + running = false; + + return true; +} + +int ProcessPool::wait() { + pid_t new_pid, reload_worker_pid = 0; + while (running) { - pid = ::wait(&status); - if (SwooleG.signal_alarm && SwooleTG.timer) { - SwooleG.signal_alarm = false; - SwooleTG.timer->select(); + ExitStatus exit_status = wait_process(); + + if (sw_timer()) { + sw_timer()->select(); + } + if (read_message) { + EventData msg; + while (pop_message(&msg, sizeof(msg)) > 0) { + if (!running) { + continue; + } + 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) { + swoole_sys_warning("fork worker process failed"); + return SW_ERR; + } + map_->erase(worker_stop_msg.pid); + } + read_message = false; } - if (pid < 0) { + if (exit_status.get_pid() < 0) { if (!running) { break; } 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) { - swoole_timer_add((long) (max_wait_time * 1000), false, ProcessPool_kill_timeout_worker, this); + swoole_timer_add((long) (max_wait_time * 1000), false, kill_timeout_worker, this); } } goto _kill_worker; @@ -635,33 +924,29 @@ int ProcessPool::wait() { } if (running) { - auto iter = map_->find(pid); - if (iter == map_->end()) { + Worker *exit_worker = get_worker_by_pid(exit_status.get_pid()); + if (exit_worker == nullptr) { if (onWorkerNotFound) { - onWorkerNotFound(this, pid, status); + onWorkerNotFound(this, exit_status); } else { - swWarn("[Manager]unknow worker[pid=%d]", pid); + swoole_warning("unknown worker[pid=%d]", exit_status.get_pid()); } continue; } - Worker *exit_worker = iter->second; - if (!WIFEXITED(status)) { - swWarn("worker#%d abnormal exit, status=%d, signal=%d" - "%s", - exit_worker->id, - WEXITSTATUS(status), - WTERMSIG(status), - WTERMSIG(status) == SIGSEGV ? "\n" SWOOLE_BUG_REPORT : ""); + if (!exit_status.is_normal_exit()) { + 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"); - sw_free(reload_workers); + swoole_sys_warning("Fork worker process failed"); return SW_ERR; } - map_->erase(pid); - if (pid == reload_worker_pid) { + map_->erase(exit_status.get_pid()); + if (exit_status.get_pid() == reload_worker_pid) { reload_worker_i++; } } @@ -675,19 +960,16 @@ 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; } } } - sw_free(reload_workers); - reload_workers = nullptr; return SW_OK; } @@ -715,17 +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 f3fb7a99b8f..aa16756ad2f 100644 --- a/src/os/sendfile.cc +++ b/src/os/sendfile.cc @@ -10,16 +10,16 @@ | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ - | Author: Tianfeng Han | + | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #include "swoole.h" -#ifdef HAVE_KQUEUE +#if defined(HAVE_KQUEUE) && defined(HAVE_SENDFILE) #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 23c743c3773..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 | +----------------------------------------------------------------------+ */ @@ -19,37 +19,55 @@ #include "swoole_signal.h" #include "swoole_socket.h" #include "swoole_reactor.h" -#include "swoole_string.h" #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 void swSignalfd_clear(); -static int swSignalfd_onSignal(Reactor *reactor, swEvent *event); +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 -static sigset_t signalfd_mask; -static int signal_fd = 0; -static swoole::network::Socket *signal_socket = nullptr; -#elif HAVE_KQUEUE -static swSignalHandler swKqueueSignal_set(int signo, swSignalHandler handler); -#else -static swSignalHandler swSignal_set(int signo, swSignalHandler func, int restart, int mask); +#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); -static swSignal signals[SW_SIGNO_MAX]; +#ifdef HAVE_SIGNALFD +static sigset_t signalfd_mask; +static int signal_fd = -1; +static pid_t signalfd_create_pid; +static Socket *signal_socket = nullptr; +static inline bool swoole_signalfd_is_available() { + return signal_fd != -1; +} +#endif +static Signal signals[SW_SIGNO_MAX]; static int _lock = 0; -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) { @@ -62,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; @@ -84,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; @@ -99,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 { @@ -114,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); } } } @@ -187,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)); } @@ -195,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,91 +235,110 @@ static swSignalHandler swSignalfd_set(int signo, swSignalHandler handler) { signals[signo].signo = signo; signals[signo].activated = true; } - if (signal_fd > 0) { + + 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); - } else if (sw_reactor()) { - swSignalfd_setup(sw_reactor()); } + swoole_signalfd_setup(sw_reactor()); return origin_handler; } -int swSignalfd_setup(Reactor *reactor) { - if (signal_fd != 0) { - return SW_OK; +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"); - return SW_ERR; + 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"); - goto _error; + swoole_sys_warning("sigprocmask() failed"); + signal_socket->fd = -1; + signal_socket->free(); + close(signal_fd); + signal_socket = nullptr; + signal_fd = -1; + return false; } - swoole_event_set_handler(SW_FD_SIGNAL, swSignalfd_onSignal); - if (swoole_event_add(signal_socket, SW_EVENT_READ) < 0) { - goto _error; - } - reactor->set_exit_condition(Reactor::EXIT_CONDITION_SIGNALFD, [](Reactor *reactor, int &event_num) -> bool { - event_num--; - return true; - }); - + signalfd_create_pid = getpid(); SwooleG.signal_fd = signal_fd; - return SW_OK; - -_error: - signal_socket->fd = -1; - signal_socket->free(); - close(signal_fd); - signal_fd = 0; + return true; +} - return SW_ERR; +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, swoole_signalfd_event_callback); + reactor->set_exit_condition(Reactor::EXIT_CONDITION_SIGNALFD, [](Reactor *reactor, size_t &event_num) -> bool { + event_num--; + return true; + }); + reactor->add_destroy_callback([](void *) { + // child process removes signal socket, parent process will not be able to trigger signal + if (signal_socket && signalfd_create_pid == getpid()) { + swoole_event_del(signal_socket); + } + }); + } + if (!(signal_socket->events & SW_EVENT_READ) && swoole_event_add(signal_socket, SW_EVENT_READ) < 0) { + return false; + } + 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)); } - signal_fd = 0; + SwooleG.signal_fd = 0; + signal_fd = -1; } -static int swSignalfd_onSignal(Reactor *reactor, swEvent *event) { - int n; +static int swoole_signalfd_event_callback(Reactor *reactor, Event *event) { struct signalfd_siginfo siginfo; - n = read(event->fd, &siginfo, sizeof(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) { + } else if (handler) { handler(siginfo.ssi_signo); } else { 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)); } } @@ -304,16 +350,16 @@ 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)); - EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, nullptr); + sw_memset_zero(&signals[signo], sizeof(Signal)); + EV_SET(&ev, signo, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); } // add/update signal else { @@ -322,12 +368,17 @@ static swSignalHandler swKqueueSignal_set(int signo, swSignalHandler handler) { signals[signo].handler = handler; signals[signo].signo = signo; signals[signo].activated = true; +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) + auto sigptr = &signals[signo]; +#else + auto sigptr = reinterpret_cast(&signals[signo]); +#endif // save swSignal* as udata - EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, &signals[signo]); + EV_SET(&ev, signo, EVFILT_SIGNAL, EV_ADD, 0, 0, sigptr); } 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 54e4e11ff98..a4bca6641ad 100644 --- a/src/os/timer.cc +++ b/src/os/timer.cc @@ -10,23 +10,23 @@ | to obtain it through the world-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_timer.h" #include "swoole_signal.h" -#include "swoole_util.h" + #include 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 39cabd75753..c1db228dedd 100644 --- a/src/os/unix_socket.cc +++ b/src/os/unix_socket.cc @@ -10,20 +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 | +----------------------------------------------------------------------+ */ #include "swoole_pipe.h" #include "swoole_socket.h" -#include - 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])) { @@ -41,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 45867d8cfc4..ca02eee1388 100644 --- a/src/os/wait.cc +++ b/src/os/wait.cc @@ -10,12 +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.h" #include "swoole_api.h" - +#include "swoole_process_pool.h" #include "swoole_coroutine.h" #include "swoole_coroutine_system.h" #include "swoole_signal.h" @@ -32,36 +32,40 @@ 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; static void signal_handler(int signo) { - if (signo == SIGCHLD) { - int __stat_loc; - - while (true) { - pid_t __pid = waitpid(-1, &__stat_loc, WNOHANG); - if (__pid <= 0) { - break; - } - - WaitTask *task = nullptr; - if (waitpid_map.find(__pid) != waitpid_map.end()) { - task = waitpid_map[__pid]; - } else if (!wait_list.empty()) { - task = wait_list.front(); - } else { - child_processes[__pid] = __stat_loc; - } - - if (task) { - task->status = __stat_loc; - task->pid = __pid; - task->co->resume(); - } + if (signo != SIGCHLD) { + return; + } + + while (true) { + auto exit_status = swoole::wait_process(-1, WNOHANG); + if (exit_status.get_pid() <= 0) { + break; + } + + WaitTask *task = nullptr; + if (waitpid_map.find(exit_status.get_pid()) != waitpid_map.end()) { + task = waitpid_map[exit_status.get_pid()]; + } else if (!wait_list.empty()) { + task = wait_list.front(); + } else { + child_processes[exit_status.get_pid()] = exit_status.get_status(); + } + + if (task) { + task->status = exit_status.get_status(); + task->pid = exit_status.get_pid(); + task->co->resume(); } } } @@ -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,23 @@ 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() + */ pid_t System::waitpid(pid_t __pid, int *__stat_loc, int __options, double timeout) { if (__pid < 0) { if (!child_processes.empty()) { @@ -120,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; } @@ -139,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; @@ -148,7 +164,14 @@ pid_t System::waitpid(pid_t __pid, int *__stat_loc, int __options, double timeou task.co); } - task.co->yield(); + Coroutine::CancelFunc cancel_fn = [timer](Coroutine *co) { + if (timer) { + swoole_timer_del(timer); + } + co->resume(); + return true; + }; + task.co->yield(&cancel_fn); /* dequeue */ if (__pid < 0) { @@ -169,7 +192,8 @@ pid_t System::waitpid(pid_t __pid, int *__stat_loc, int __options, double timeou } *__stat_loc = task.status; } else { - errno = ETIMEDOUT; + swoole_set_last_error(task.co->is_canceled() ? SW_ERROR_CO_CANCELED : ETIMEDOUT); + errno = swoole_get_last_error(); } return task.pid; 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 06972e5ac05..aa5d8c68335 100644 --- a/src/protocol/http.cc +++ b/src/protocol/http.cc @@ -10,54 +10,134 @@ | to obtain it through the world-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 -#include +#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::network::Socket; -using swoole::network::SendfileTask; using swoole::http_server::Request; using swoole::http_server::StaticHandler; +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) { @@ -72,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, @@ -81,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; @@ -93,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); @@ -112,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(), @@ -163,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; @@ -188,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 @@ -206,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"; @@ -298,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: @@ -326,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; @@ -346,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; @@ -354,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 { @@ -368,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; @@ -398,9 +795,6 @@ char *swHttp_url_encode(char const *str, size_t len) { return ret; } -namespace swoole { -namespace http_server { -//----------------------------------------------------------------- /** * only GET/POST */ @@ -462,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; @@ -472,9 +865,7 @@ int Request::get_protocol() { } else { goto _excepted; } - } -#endif - else { + } else { _excepted: excepted = 1; return SW_ERR; @@ -502,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) { @@ -535,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; + } } } } @@ -576,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; @@ -603,7 +1076,6 @@ bool Request::has_expect_header() { } return false; } -#endif int Request::get_header_length() { char *p = buffer_->str + buffer_->offset; @@ -626,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; } @@ -655,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: @@ -690,22 +1178,35 @@ string Request::get_date_if_modified_since() { } } - return string(""); + return string(); } -//----------------------------------------------------------------- -} // namespace http -} // 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; +} + +const char *get_method_string(int method) { + if (method < 0 || method > SW_HTTP_PRI) { + return nullptr; + } + return method_strings[method - 1]; } -#ifdef SW_USE_HTTP2 +int dispatch_request(Server *serv, const Protocol *proto, Socket *_socket, const RecvData *rdata) { + if (serv->is_unavailable()) { + _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0); + return SW_ERR; + } + return Server::dispatch_task(proto, _socket, rdata); +} + +//----------------------------------------------------------------- static void protocol_status_error(Socket *socket, Connection *conn) { swoole_error_log(SW_LOG_WARNING, SW_ERROR_PROTOCOL_ERROR, @@ -715,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 swWebSocket_get_package_length(protocol, socket, data, length); + if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { + return websocket::get_package_length(protocol, socket, pl); } else if (conn->http2_stream) { - return swHttp2_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); + if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { + return SW_WEBSOCKET_MESSAGE_HEADER_SIZE; } else if (conn->http2_stream) { return SW_HTTP2_FRAME_HEADER_SIZE; } else { @@ -739,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 swWebSocket_dispatch_frame(proto, socket, data, length); + if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) { + 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 234918292e4..b870eda4eb4 100644 --- a/src/protocol/http2.cc +++ b/src/protocol/http2.cc @@ -12,44 +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; -int swHttp2_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; +namespace swoole { +namespace http2 { + +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)); +} - swHttp2_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); } /** @@ -63,14 +138,14 @@ int swHttp2_send_setting_frame(Protocol *protocol, Socket *_socket) { | Frame Payload (0...) ... +---------------------------------------------------------------+ */ -ssize_t swHttp2_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 swHttp2_get_length(buf) + SW_HTTP2_FRAME_HEADER_SIZE; + return get_length(pl->buf) + SW_HTTP2_FRAME_HEADER_SIZE; } -const char *swHttp2_get_type(int type) { +const char *get_type(int type) { switch (type) { case SW_HTTP2_TYPE_DATA: return "DATA"; @@ -93,11 +168,11 @@ const char *swHttp2_get_type(int type) { case SW_HTTP2_TYPE_CONTINUATION: return "CONTINUATION"; default: - return "UNKOWN"; + return "UNKNOWN"; } } -int swHttp2_get_type_color(int type) { +int get_type_color(int type) { switch (type) { case SW_HTTP2_TYPE_DATA: case SW_HTTP2_TYPE_WINDOW_UPDATE: @@ -116,3 +191,6 @@ int swHttp2_get_type_color(int type) { return SW_COLOR_RED; } } + +} // namespace http2 +} // namespace swoole 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 1c780c4ac53..f58939666c8 100644 --- a/src/protocol/socks5.cc +++ b/src/protocol/socks5.cc @@ -12,13 +12,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.h" #include "swoole_proxy.h" -#include "swoole_client.h" + namespace swoole { const char *Socks5Proxy::strerror(int code) { switch (code) { diff --git a/src/protocol/ssl.cc b/src/protocol/ssl.cc index c69d30a1515..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,33 +18,35 @@ #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" #endif -static int openssl_init = 0; +static bool openssl_init = false; +static bool openssl_thread_safety_init = false; 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__ @@ -53,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); +} + +static void MAYBE_UNUSED swoole_ssl_lock_callback(int mode, int type, const char *file, int line); -void swSSL_init(void) { +void swoole_ssl_init(void) { if (openssl_init) { return; } @@ -71,47 +77,49 @@ 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 = 1; + 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; } - CRYPTO_set_locking_callback(nullptr); - int i; - for (i = 0; i < CRYPTO_num_locks(); i++) { + SW_LOOP_N(CRYPTO_num_locks()) { pthread_mutex_destroy(&(lock_array[i])); } - openssl_init = 0; + + OPENSSL_free(lock_array); + #if OPENSSL_VERSION_NUMBER >= OPENSSL_VERSION_1_0_0 (void) CRYPTO_THREADID_set_callback(nullptr); #else CRYPTO_set_id_callback(nullptr); #endif CRYPTO_set_locking_callback(nullptr); + openssl_init = false; + 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 { @@ -125,42 +133,51 @@ 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; } - int i; + + if (openssl_thread_safety_init) { + return; + } + lock_array = (pthread_mutex_t *) OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); - for (i = 0; i < CRYPTO_num_locks(); i++) { + SW_LOOP_N(CRYPTO_num_locks()) { pthread_mutex_init(&(lock_array[i]), nullptr); } #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; } -static void swSSL_info_callback(const SSL *ssl, int where, int ret) { +bool swoole_ssl_is_thread_safety() { + return openssl_thread_safety_init; +} + +static void swoole_ssl_info_callback(const SSL *ssl, int where, int ret) { BIO *rbio, *wbio; swSocket *sock; @@ -169,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"); } } @@ -203,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) { @@ -225,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; @@ -255,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) { @@ -284,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; @@ -302,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; } @@ -398,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); @@ -411,7 +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() failed, Error: %s[%d]", 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; } /* @@ -420,49 +438,64 @@ 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() failed, Error: %s[%d]", 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; } + } + if (!key_file.empty()) { /* * set the private key from KeyFile (may be the same as CertFile) */ 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() failed, Error: %s[%d]", 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 if (verify_peer && !set_capath()) { return false; + } else { + SSL_CTX_set_verify(context, SSL_VERIFY_NONE, nullptr); } #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; } @@ -476,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; } @@ -491,12 +528,14 @@ bool SSLContext::create() { bool SSLContext::set_capath() { if (!cafile.empty() || !capath.empty()) { - if (!SSL_CTX_load_verify_locations(context, cafile.c_str(), capath.c_str())) { + const char *_cafile = cafile.empty() ? nullptr : cafile.c_str(); + const char *_capath = capath.empty() ? nullptr : capath.c_str(); + if (!SSL_CTX_load_verify_locations(context, _cafile, _capath)) { return false; } } 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; } } @@ -515,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) { @@ -524,7 +563,7 @@ bool SSLContext::set_ciphers() { } #ifndef OPENSSL_NO_RSA - SSL_CTX_set_tmp_rsa_callback(ssl_context, swSSL_rsa_key_callback); + SSL_CTX_set_tmp_rsa_callback(context, swoole_ssl_rsa_key_callback); #endif if (!dhparam.empty() && !set_dhparam()) { @@ -532,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()) { @@ -547,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; } @@ -597,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 @@ -610,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; } @@ -638,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; } @@ -661,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; @@ -679,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) { @@ -694,8 +733,6 @@ static int swSSL_verify_callback(int ok, X509_STORE_CTX *x509_store) { return 1; } - - #ifdef SW_SUPPORT_DTLS #define COOKIE_SECRET_LENGTH (32) @@ -710,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{}; @@ -738,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; } @@ -765,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; @@ -784,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; } @@ -801,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, @@ -816,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 d67446ed936..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 | +----------------------------------------------------------------------+ */ @@ -18,13 +18,15 @@ #include "swoole_server.h" #include "swoole_websocket.h" -using swoole::Server; using swoole::Connection; -using swoole::String; using swoole::Protocol; +using swoole::Server; +using swoole::String; using swoole::network::Socket; -static inline uint16_t swWebSocket_get_ext_flags(uchar opcode, uchar flags) { +namespace swoole { +namespace websocket { +static inline uint16_t get_ext_flags(uchar opcode, uchar flags) { uint16_t ext_flags = opcode; ext_flags = ext_flags << 8; ext_flags += flags; @@ -52,51 +54,54 @@ static inline uint16_t swWebSocket_get_ext_flags(uchar opcode, uchar flags) { | Payload Data continued ... | +---------------------------------------------------------------+ */ - -ssize_t swWebSocket_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=%u", 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; } -static sw_inline void swWebSocket_mask(char *data, size_t len, const char *mask_key) { +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) { size_t n = len / 8; uint64_t mask_key64 = ((uint64_t)(*((uint32_t *) mask_key)) << 32) | *((uint32_t *) mask_key); size_t i; @@ -110,21 +115,21 @@ static sw_inline void swWebSocket_mask(char *data, size_t len, const char *mask_ } } -bool swWebSocket_encode(String *buffer, const char *data, size_t length, char opcode, uint8_t _flags) { +bool encode(String *buffer, const char *data, size_t length, char opcode, uint8_t _flags) { int pos = 0; char frame_header[16]; - swWebSocket_frame_header *header = (swWebSocket_frame_header *) frame_header; - header->FIN = !!(_flags & SW_WEBSOCKET_FLAG_FIN); + Header *header = (Header *) frame_header; + header->FIN = !!(_flags & FLAG_FIN); header->OPCODE = opcode; - header->RSV1 = !!(_flags & SW_WEBSOCKET_FLAG_RSV1); + header->RSV1 = !!(_flags & FLAG_RSV1); header->RSV2 = 0; header->RSV3 = 0; - header->MASK = !!(_flags & SW_WEBSOCKET_FLAG_MASK); + 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); @@ -141,17 +146,17 @@ bool swWebSocket_encode(String *buffer, const char *data, size_t length, char op */ if (header->MASK) { buffer->append(SW_WEBSOCKET_MASK_DATA, SW_WEBSOCKET_MASK_LEN); - if (_flags & SW_WEBSOCKET_FLAG_ENCODE_HEADER_ONLY) { + if (_flags & FLAG_ENCODE_HEADER_ONLY) { return false; } if (length > 0) { size_t offset = buffer->length; // Warn: buffer may be extended, string pointer will change buffer->append(data, length); - swWebSocket_mask(buffer->str + offset, length, SW_WEBSOCKET_MASK_DATA); + mask(buffer->str + offset, length, SW_WEBSOCKET_MASK_DATA); } } else { - if (length > 0 and !(_flags & SW_WEBSOCKET_FLAG_ENCODE_HEADER_ONLY)) { + if (length > 0 and !(_flags & FLAG_ENCODE_HEADER_ONLY)) { buffer->append(data, length); } } @@ -159,49 +164,51 @@ bool swWebSocket_encode(String *buffer, const char *data, size_t length, char op return true; } -bool swWebSocket_decode(swWebSocket_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; +bool decode(Frame *frame, char *data, size_t length) { + 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; } - if (payload_length == 0) { - frame->header_length = header_length; - frame->payload_length = 0; - frame->payload = nullptr; + frame->payload_length = total_length - pl.header_len; + frame->header_length = pl.header_len; - return true; - } + swoole_trace_log(SW_TRACE_WEBSOCKET, + "decode frame, payload_length=%ld, mask=%d, opcode=%d", + frame->payload_length, + frame->header.MASK, + frame->header.OPCODE); - if (frame->header.MASK) { - memcpy(frame->mask_key, data + header_length, SW_WEBSOCKET_MASK_LEN); - header_length += SW_WEBSOCKET_MASK_LEN; - swWebSocket_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 swWebSocket_pack_close_frame(String *buffer, int code, char *reason, size_t length, uint8_t flags) { +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; } @@ -211,12 +218,14 @@ int swWebSocket_pack_close_frame(String *buffer, int code, char *reason, size_t if (length > 0) { memcpy(payload + SW_WEBSOCKET_CLOSE_CODE_LEN, reason, length); } - flags |= SW_WEBSOCKET_FLAG_FIN; - swWebSocket_encode(buffer, payload, SW_WEBSOCKET_CLOSE_CODE_LEN + length, WEBSOCKET_OPCODE_CLOSE, flags); + flags |= FLAG_FIN; + if (!encode(buffer, payload, SW_WEBSOCKET_CLOSE_CODE_LEN + length, OPCODE_CLOSE, flags)) { + return SW_ERR; + } return SW_OK; } -void swWebSocket_print_frame(swWebSocket_frame *frame) { +void print_frame(Frame *frame) { printf("FIN: %x, RSV1: %d, RSV2: %d, RSV3: %d, opcode: %d, MASK: %d, length: %ld\n", frame->header.FIN, frame->header.RSV1, @@ -231,28 +240,32 @@ void swWebSocket_print_frame(swWebSocket_frame *frame) { } } -int swWebSocket_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); - swWebSocket_frame ws; - swWebSocket_decode(&ws, const_cast(data), length); + Frame ws; + 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 WEBSOCKET_OPCODE_CONTINUATION: + 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; @@ -260,71 +273,75 @@ int swWebSocket_dispatch_frame(Protocol *proto, Socket *_socket, const char *dat 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 |= SW_WEBSOCKET_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; } break; - case WEBSOCKET_OPCODE_TEXT: - case WEBSOCKET_OPCODE_BINARY: { + case OPCODE_TEXT: + case OPCODE_BINARY: { offset = length - ws.payload_length; - proto->ext_flags = swWebSocket_get_ext_flags(ws.header.OPCODE, swWebSocket_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 WEBSOCKET_OPCODE_PING: - case WEBSOCKET_OPCODE_PONG: + 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 == WEBSOCKET_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 = swWebSocket_get_ext_flags(ws.header.OPCODE, swWebSocket_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 WEBSOCKET_OPCODE_CLOSE: + case OPCODE_CLOSE: if ((length - SW_WEBSOCKET_HEADER_LEN) > SW_WEBSOCKET_CLOSE_REASON_MAX_LEN) { return SW_ERR; } - if (conn->websocket_status != WEBSOCKET_STATUS_CLOSING) { + if (conn->websocket_status != STATUS_CLOSING) { // Dispatch the frame with the same format of message frame offset = length - ws.payload_length; - proto->ext_flags = swWebSocket_get_ext_flags(ws.header.OPCODE, swWebSocket_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 @@ -341,8 +358,10 @@ int swWebSocket_dispatch_frame(Protocol *proto, Socket *_socket, const char *dat return SW_ERR; default: - swWarn("unknown opcode [%d]", ws.header.OPCODE); + swoole_warning("unknown opcode [%d]", ws.header.OPCODE); break; } return SW_OK; } +} // namespace websocket +} // namespace swoole diff --git a/src/reactor/base.cc b/src/reactor/base.cc index ea44d8b6792..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 | +----------------------------------------------------------------------+ */ @@ -18,12 +18,9 @@ #include "swoole_socket.h" #include "swoole_signal.h" #include "swoole_reactor.h" -#include "swoole_timer.h" #include "swoole_api.h" #include "swoole_c_api.h" -#include - namespace swoole { using network::Socket; @@ -51,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); } } @@ -113,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; @@ -126,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); @@ -158,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("unknow fdtype"); + swoole_warning("unknown fdtype"); return false; } @@ -185,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; @@ -205,33 +202,22 @@ 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; int fd = socket->fd; if (socket->buffer_size == 0) { - socket->buffer_size = Socket::default_buffer_size; + socket->set_memory_buffer_size(Socket::default_buffer_size); } if (socket->nonblock == 0) { @@ -260,18 +246,19 @@ 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; } - - reactor->add_write_event(socket); + if (!socket->isset_writable_event()) { + reactor->add_write_event(socket); + } goto _append_buffer; } else if (errno == EINTR) { goto _do_send; @@ -294,23 +281,23 @@ 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); return send_bytes; }; - auto append_fn = [&send_bytes, socket, buf, n](Buffer *buffer) { + auto append_fn = [&send_bytes, buf, n](Buffer *buffer) { ssize_t offset = send_bytes > 0 ? send_bytes : 0; buffer->append((const char *) buf + offset, n - offset); }; 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"); @@ -327,7 +314,7 @@ int Reactor::_writev(Reactor *reactor, network::Socket *socket, const iovec *iov send_bytes = socket->writev(iov, iovcnt); return send_bytes; }; - auto append_fn = [&send_bytes, socket, iov, iovcnt](Buffer *buffer) { + auto append_fn = [&send_bytes, iov, iovcnt](Buffer *buffer) { ssize_t offset = send_bytes > 0 ? send_bytes : 0; buffer->append(iov, iovcnt, offset); }; @@ -343,9 +330,7 @@ int Reactor::_writable_callback(Reactor *reactor, Event *ev) { while (!Buffer::empty(buffer)) { BufferChunk *chunk = buffer->front(); if (chunk->type == BufferChunk::TYPE_CLOSE) { - _close_fd: - reactor->close(reactor, ev->socket); - return SW_OK; + return reactor->close(reactor, ev->socket); } else if (chunk->type == BufferChunk::TYPE_SENDFILE) { ret = socket->handle_sendfile(); } else { @@ -354,7 +339,7 @@ int Reactor::_writable_callback(Reactor *reactor, Event *ev) { if (ret < 0) { if (socket->close_wait) { - goto _close_fd; + return reactor->trigger_close_event(ev); } else if (socket->send_wait) { return SW_OK; } @@ -374,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); @@ -398,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; } @@ -419,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 8a5b77bdf88..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,10 +18,6 @@ #include "swoole_socket.h" #include "swoole_reactor.h" -#include - -#define EVENT_DEBUG 0 - #ifdef HAVE_EPOLL #include #ifndef EPOLLRDHUP @@ -62,21 +58,12 @@ class ReactorEpoll : public ReactorImpl { events |= EPOLLONESHOT; } if (Reactor::isset_error_event(fdtype)) { - // flag |= (EPOLLRDHUP); events |= (EPOLLRDHUP | EPOLLHUP | EPOLLERR); } return events; } }; -#if EVENT_DEBUG -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); } @@ -84,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; } @@ -111,18 +98,14 @@ 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( - SW_TRACE_EVENT, "add events[fd=%d#%d, type=%d, events=%d]", socket->fd, reactor_->id, socket->fd_type, events_); + 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; } @@ -131,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) { @@ -142,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; @@ -160,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; @@ -196,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; @@ -219,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 @@ -227,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 @@ -239,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 5b0e01adc52..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,31 +102,37 @@ int ReactorKqueue::add(Socket *socket, int events) { int fd = socket->fd; int fflags = 0; +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) + auto sobj = socket; +#else + auto sobj = reinterpret_cast(socket); +#endif + if (Reactor::isset_read_event(events)) { #ifdef NOTE_EOF fflags = NOTE_EOF; #endif - EV_SET(&e, fd, EVFILT_READ, EV_ADD, fflags, 0, socket); + 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; } } if (Reactor::isset_write_event(events)) { - EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, socket); + 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; } @@ -135,43 +144,49 @@ int ReactorKqueue::set(Socket *socket, int events) { int fd = socket->fd; int fflags = 0; +#if !defined(__NetBSD__) || (defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000) + auto sobj = socket; +#else + auto sobj = reinterpret_cast(socket); +#endif + if (Reactor::isset_read_event(events)) { #ifdef NOTE_EOF fflags = NOTE_EOF; #endif - EV_SET(&e, fd, EVFILT_READ, EV_ADD, fflags, 0, socket); + 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, 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); return SW_ERR; } } if (Reactor::isset_write_event(events)) { - EV_SET(&e, fd, EVFILT_WRITE, EV_ADD, 0, 0, socket); + 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, socket); + 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; } @@ -181,6 +196,12 @@ int ReactorKqueue::del(Socket *socket) { int ret; int fd = socket->fd; +#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); @@ -188,10 +209,10 @@ int ReactorKqueue::del(Socket *socket) { } if (socket->events & SW_EVENT_READ) { - EV_SET(&e, fd, EVFILT_READ, EV_DELETE, 0, 0, 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; } @@ -199,7 +220,7 @@ int ReactorKqueue::del(Socket *socket) { } if (socket->events & SW_EVENT_WRITE) { - EV_SET(&e, fd, EVFILT_WRITE, EV_DELETE, 0, 0, socket); + EV_SET(&e, fd, EVFILT_WRITE, EV_DELETE, 0, 0, sobj); ret = ::kevent(epfd_, &e, 1, nullptr, 0, nullptr); if (ret < 0) { after_removal_failure(socket); @@ -210,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; } @@ -252,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; @@ -262,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]; @@ -277,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); } @@ -286,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 34f39ab347e..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 || conn->active == 0) { + 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, 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 ccf3681b9b9..5bdea0ec9fc 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -10,17 +10,20 @@ | to obtain it through the world-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_util.h" + #include #include -#include -#ifdef __linux__ +#if defined(__linux__) #include +#elif defined(__FreeBSD__) +#include #endif namespace swoole { @@ -28,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; - int 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); @@ -52,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); } } @@ -67,13 +66,13 @@ void Manager::kill_timeout_process(Timer *timer, TimerNode *tnode) { continue; } if (swoole_kill(pid, SIGKILL) < 0) { - swSysWarn("swKill(%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, - "[Manager] Worker#%d[pid=%d] exit timeout, force kill the process", - worker_id, - pid); + "worker(pid=%d, id=%d) exit timeout, force kill the process", + pid, + worker_id); } } errno = 0; @@ -98,151 +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; - } - close_port(true); + 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{}; - exit(manager.start(this)); - break; - } - // master process - default: - gs->manager_pid = pid; - break; - case -1: - swError("fork() failed"); - return SW_ERR; - } - return SW_OK; -} - -void Server::check_worker_exit_status(int worker_id, pid_t pid, int status) { - if (status != 0) { - swWarn("worker#%d[pid=%d] abnormal exit, status=%d, signal=%d" - "%s", - worker_id, - pid, - WEXITSTATUS(status), - WTERMSIG(status), - WTERMSIG(status) == SIGSEGV ? "\n" SWOOLE_BUG_REPORT : ""); - if (onWorkerError != nullptr) { - onWorkerError(this, worker_id, pid, status); + manager.wait(this); + }; + + 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; } -int 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 = (Worker *) sw_calloc(_server->worker_num + _server->task_worker_num, sizeof(Worker)); - if (reload_workers == nullptr) { - swError("malloc[reload_workers] failed"); - return SW_ERR; - } + pool->reload_workers = new Worker[_server->worker_num + _server->task_worker_num]; + ON_SCOPE_EXIT { + 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); -#ifdef __linux__ - prctl(PR_SET_PDEATHSIG, SIGTERM); + + if (_server->is_process_mode()) { +#if defined(__linux__) + prctl(PR_SET_PDEATHSIG, SIGTERM); +#elif defined(__FreeBSD__) + 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); } @@ -255,56 +209,57 @@ int Manager::start(Server *_server) { } while (_server->running) { - int status; - pid_t pid = wait(&status); - - if (read_message) { - WorkerStopMessage msg; - while (_server->message_box->pop(&msg, sizeof(msg)) > 0) { + ExitStatus exit_status = wait_process(); + 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 (pid < 0) { - if (!reloading) { + if (exit_status.get_pid() < 0) { + 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; @@ -315,13 +270,14 @@ int 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; @@ -329,21 +285,22 @@ int 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; @@ -354,68 +311,64 @@ int Manager::start(Server *_server) { if (_server->running) { // event workers SW_LOOP_N(_server->worker_num) { + Worker *worker = _server->get_worker(i); // find worker - if (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, pid, 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(pid); + 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, pid, 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) { - Server::wait_other_worker(&_server->gs->event_workers, pid, status); + if (!_server->user_worker_map.empty()) { + Server::wait_other_worker(&_server->gs->event_workers, exit_status); } - if (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); } } } - sw_free(reload_workers); - if (SwooleTG.timer) { swoole_timer_free(); } @@ -430,8 +383,8 @@ int 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); } } @@ -440,9 +393,9 @@ int 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); @@ -450,56 +403,44 @@ int Manager::start(Server *_server) { if (_server->onManagerStop) { _server->onManagerStop(_server); } +} - return SW_OK; +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 sig) { +void Manager::signal_handler(int signo) { Server *_server = sw_server(); - if (!_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 @@ -510,23 +451,23 @@ void Manager::signal_handler(int sig) { /** * @return: success returns pid, failure returns SW_ERR. */ -int Server::wait_other_worker(ProcessPool *pool, pid_t pid, int status) { +int Server::wait_other_worker(ProcessPool *pool, const ExitStatus &exit_status) { Server *serv = (Server *) pool->ptr; Worker *exit_worker = nullptr; int worker_type; do { if (serv->gs->task_workers.map_) { - auto iter = serv->gs->task_workers.map_->find(pid); + auto iter = serv->gs->task_workers.map_->find(exit_status.get_pid()); if (iter != serv->gs->task_workers.map_->end()) { worker_type = SW_PROCESS_TASKWORKER; exit_worker = iter->second; break; } } - if (serv->user_worker_map) { - auto iter = serv->user_worker_map->find(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; @@ -535,16 +476,16 @@ int Server::wait_other_worker(ProcessPool *pool, pid_t pid, int status) { return SW_ERR; } while (0); - serv->check_worker_exit_status(exit_worker->id, pid, 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 */ @@ -555,113 +496,61 @@ int Server::wait_other_worker(ProcessPool *pool, pid_t pid, int 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)); + if (gs->manager_pid == 0) { + swoole_error_log(SW_LOG_WARNING, SW_ERROR_OPERATION_NOT_SUPPORT, "not supported with single process mode"); + return false; } - // parent,add to writer - else { - return pid; - } -} -pid_t Server::spawn_user_worker(Worker *worker) { - pid_t pid = swoole_fork(0); - - if (pid < 0) { - 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(); - // close tcp listen socket - if (is_base_mode()) { - close_port(true); - } - 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 7bcd34a5d30..b7cfa038d6e 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -10,13 +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_server.h" #include "swoole_memory.h" -#include "swoole_http.h" #include "swoole_lock.h" #include "swoole_util.h" @@ -30,16 +29,6 @@ swoole::Server *g_server_instance = nullptr; namespace swoole { -static void Server_signal_handler(int sig); - -static void **Server_worker_create_buffers(Server *serv, uint32_t buffer_num); -static void Server_worker_free_buffers(Server *serv, uint32_t buffer_num, void **buffers); -static void *Server_worker_get_buffer(Server *serv, DataHead *info); -static size_t Server_worker_get_buffer_len(Server *serv, DataHead *info); -static void Server_worker_add_buffer_len(Server *serv, DataHead *info, size_t len); -static void Server_worker_move_buffer(Server *serv, PipeBuffer *buffer); -static size_t Server_worker_get_packet(Server *serv, EventData *req, char **data_ptr); - TimerCallback Server::get_timeout_callback(ListenPort *port, Reactor *reactor, Connection *conn) { return [this, port, conn, reactor](Timer *, TimerNode *) { if (conn->protect) { @@ -64,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; @@ -90,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(); @@ -118,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; } @@ -141,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; @@ -149,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 { @@ -158,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 { @@ -166,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; - if (serv->send_to_reactor_thread((EventData*) &ev, sizeof(ev), conn->session_id) < 0) { - reactor->close(reactor, sock); + ev.server_fd = event->fd; + if (serv->send_to_reactor_thread((EventData *) &ev, sizeof(ev), conn->session_id) < 0) { + serv->abort_connection(reactor, listen_host, sock); return SW_OK; } } @@ -181,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) { @@ -229,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; } @@ -271,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; @@ -279,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() { @@ -292,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; @@ -317,38 +384,98 @@ 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; } } if (send_timeout > 0 && send_timeout < SW_TIMER_MIN_SEC) { send_timeout = SW_TIMER_MIN_SEC; } + if (heartbeat_check_interval > 0) { + for (auto ls : ports) { + if (ls->heartbeat_idle_time == 0) { + ls->heartbeat_idle_time = heartbeat_check_interval * 2; + } + } + } for (auto ls : ports) { if (ls->protocol.package_max_length < SW_BUFFER_MIN_SIZE) { 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) { + int expect_heartbeat_check_interval = ls->heartbeat_idle_time > 2 ? ls->heartbeat_idle_time / 2 : 1; + if (heartbeat_check_interval == 0 || heartbeat_check_interval > expect_heartbeat_check_interval) { + heartbeat_check_interval = expect_heartbeat_check_interval; + } + } } #ifdef SW_USE_OPENSSL /** * OpenSSL thread-safe */ - if (is_base_mode()) { - 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; @@ -358,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); @@ -365,41 +493,26 @@ void Server::store_listen_socket() { } } -static void **Server_worker_create_buffers(Server *serv, uint32_t buffer_num) { - String **buffers = new String *[buffer_num]; - for (uint i = 0; i < buffer_num; i++) { - buffers[i] = new String(SW_BUFFER_SIZE_BIG); - } - return (void **) buffers; -} - -static void Server_worker_free_buffers(Server *serv, uint32_t buffer_num, void **_buffers) { - String **buffers = (String **) _buffers; - for (uint i = 0; i < buffer_num; i++) { - delete buffers[i]; - } - delete[] buffers; -} - /** * only the memory of the Worker structure is allocated, no process is fork */ int Server::create_task_workers() { key_t key = 0; - int 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; } ProcessPool *pool = &gs->task_workers; - if (ProcessPool::create(pool, task_worker_num, key, ipc_mode) < 0) { - swWarn("[Master] create task_workers failed"); + *pool = {}; + if (pool->create(task_worker_num, key, ipc_mode) < 0) { + swoole_warning("[Master] create task_workers failed"); return SW_ERR; } @@ -410,11 +523,29 @@ int Server::create_task_workers() { if (ipc_mode == SW_IPC_SOCKET) { char sockfile[sizeof(struct sockaddr_un)]; snprintf(sockfile, sizeof(sockfile), "/tmp/swoole.task.%d.sock", gs->master_pid); - if (gs->task_workers.create_unix_socket(sockfile, 2048) < 0) { + if (gs->task_workers.listen(sockfile, 2048) < 0) { return SW_ERR; } } + /* + * 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; @@ -424,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; } @@ -450,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) { @@ -460,63 +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(); - - worker_input_buffers = (void **) create_buffers(this, get_worker_buffer_num()); - if (!worker_input_buffers) { - swError("failed to create worker buffers"); - } - - 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. @@ -540,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"); } } @@ -549,62 +636,35 @@ int Server::start() { } } - // master pid - gs->master_pid = getpid(); gs->start_time = ::time(nullptr); /** - * store to swProcessPool object + * store to ProcessPool object */ gs->event_workers.ptr = this; gs->event_workers.workers = workers; 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()); @@ -613,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) { @@ -632,47 +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; -#endif + compression_min_length = SW_COMPRESSION_MIN_LENGTH_DEFAULT; -#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"); } - /** - * init method - */ - create_buffers = Server_worker_create_buffers; - free_buffers = Server_worker_free_buffers; - get_buffer = Server_worker_get_buffer; - get_buffer_len = Server_worker_get_buffer_len; - add_buffer_len = Server_worker_add_buffer_len; - move_buffer = Server_worker_move_buffer; - get_packet = Server_worker_get_packet; + gs->pipe_packet_msg_id = 1; + gs->max_concurrency = UINT_MAX; + + msg_id_generator = [this]() { return sw_atomic_fetch_add(&gs->pipe_packet_msg_id, 1); }; + message_bus.set_id_generator(msg_id_generator); + worker_thread_start = [](const WorkerFn &fn) { fn(); }; g_server_instance = this; } @@ -688,45 +734,53 @@ 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 uint32_t minimum_connection = (worker_num + task_worker_num) * 2 + 32; if (ports.size() > 0) { - minimum_connection += ports.back()->socket_fd; + minimum_connection += ports.back()->get_fd(); } 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; @@ -736,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; } @@ -746,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() { @@ -782,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; @@ -852,7 +1024,7 @@ void Server::destroy() { ::close(null_fd); null_fd = -1; } - swSignal_clear(); + swoole_signal_clear(); /** * shutdown status */ @@ -864,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; @@ -907,12 +1079,131 @@ bool Server::feedback(Connection *conn, enum ServerEventType event) { _send.info.reactor_id = conn->reactor_id; if (is_process_mode()) { - return send_to_reactor_thread((EventData*) &_send.info, sizeof(_send.info), conn->session_id) > 0; + return send_to_reactor_thread((EventData *) &_send.info, sizeof(_send.info), conn->session_id) > 0; } else { return send_to_connection(&_send) == SW_OK; } } +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); @@ -934,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.data = (char*) 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) { @@ -945,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) { @@ -972,37 +1276,39 @@ int Server::schedule_worker(int fd, SendData *data) { // IPv6 else { #ifdef HAVE_KQUEUE - key = *(((uint32_t*) &conn->info.addr.inet_v6.sin6_addr) + 3); + key = *(((uint32_t *) &conn->info.addr.inet_v6.sin6_addr) + 3); #elif defined(_WIN32) - key = conn->info.addr.inet_v6.sin6_addr.u.Word[3]; + key = conn->info.addr.inet_v6.sin6_addr.u.Word[3]; #else - key = conn->info.addr.inet_v6.sin6_addr.s6_addr32[3]; + 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 == DISPATCH_CO_CONN_LB) { + Connection *conn = get_connection(fd); + if (conn == nullptr) { + return key % worker_num; + } + if (conn->worker_id < 0) { + conn->worker_id = get_lowest_load_worker_id(); + } + return conn->worker_id; + } else if (dispatch_mode == DISPATCH_CO_REQ_LB) { + return get_lowest_load_worker_id(); + } else if (dispatch_mode == DISPATCH_CONCURRENT_LB) { + return get_lowest_concurrent_worker_id(); } - // 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; } @@ -1022,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, @@ -1047,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 { @@ -1132,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 @@ -1150,7 +1454,7 @@ int Server::send_to_connection(SendData *_send) { else { // connection is closed if (conn->peer_closed) { - swWarn("connection#%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 @@ -1181,12 +1485,11 @@ 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); } - // listen EPOLLOUT event - if (reactor->set(_socket, SW_EVENT_WRITE | SW_EVENT_READ) < 0 && (errno == EBADF || errno == ENOENT)) { - goto _close_fd; + if (!_socket->isset_writable_event()) { + reactor->add_write_event(_socket); } return SW_OK; @@ -1196,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; @@ -1229,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) @@ -1241,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; @@ -1264,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; @@ -1274,59 +1584,8 @@ bool Server::sendwait(SessionId session_id, const void *data, uint32_t length) { return conn->socket->send_blocking(data, length) == length; } -static sw_inline void Server_worker_set_buffer(Server *serv, DataHead *info, String *addr) { - String **buffers = (String **) serv->worker_input_buffers; - buffers[info->reactor_id] = addr; -} - -static void *Server_worker_get_buffer(Server *serv, DataHead *info) { - String *worker_buffer = serv->get_worker_input_buffer(info->reactor_id); - - if (worker_buffer == nullptr) { - worker_buffer = new String(info->len); - Server_worker_set_buffer(serv, info, worker_buffer); - } - - return worker_buffer->str + worker_buffer->length; -} - -static size_t Server_worker_get_buffer_len(Server *serv, DataHead *info) { - String *worker_buffer = serv->get_worker_input_buffer(info->reactor_id); - - return worker_buffer == nullptr ? 0 : worker_buffer->length; -} - -static void Server_worker_add_buffer_len(Server *serv, DataHead *info, size_t len) { - String *worker_buffer = serv->get_worker_input_buffer(info->reactor_id); - worker_buffer->length += len; -} - -static void Server_worker_move_buffer(Server *serv, PipeBuffer *buffer) { - String *worker_buffer = serv->get_worker_input_buffer(buffer->info.reactor_id); - memcpy(buffer->data, &worker_buffer, sizeof(worker_buffer)); - Server_worker_set_buffer(serv, &buffer->info, nullptr); -} - -static size_t Server_worker_get_packet(Server *serv, 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); } @@ -1334,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) { @@ -1375,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; } @@ -1395,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 @@ -1413,13 +1703,30 @@ void Server::check_port_type(ListenPort *ls) { } } +bool Server::is_healthy_connection(double now, Connection *conn) { + if (conn->protect || conn->last_recv_time == 0) { + return true; + } + auto lp = get_port_by_session_id(conn->session_id); + if (!lp) { + return true; + } + if (lp->heartbeat_idle_time == 0) { + return true; + } + if (conn->last_recv_time > now - lp->heartbeat_idle_time) { + return true; + } + return false; +} + /** * Return the number of ports successfully */ 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; } @@ -1435,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; } @@ -1459,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; @@ -1493,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(); @@ -1508,102 +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(); - ls->socket_fd = ls->socket->fd; 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; @@ -1619,26 +1878,76 @@ 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() ? swoole_get_process_id() : fd % reactor_num; *connection = {}; + + sw_spinlock(&gs->spinlock); + SessionId session_id = gs->session_round; + // get session id + SW_LOOP_N(max_connection) { + Session *session = get_session(++session_id); + // available slot + if (session->fd == 0) { + session->fd = fd; + session->id = session_id; + session->reactor_id = reactor_id; + goto _find_available_slot; + } + } + sw_spinlock_release(&gs->spinlock); + swoole_error_log(SW_LOG_WARNING, SW_ERROR_SERVER_TOO_MANY_SOCKET, "no available session slot, fd=%d", fd); + return nullptr; + +_find_available_slot: + sw_spinlock_release(&gs->spinlock); + gs->session_round = session_id; + connection->session_id = session_id; + _socket->object = connection; _socket->removed = 1; _socket->buffer_size = ls->socket_buffer_size; @@ -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,22 +1964,23 @@ 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); } } connection->fd = fd; - connection->reactor_id = is_base_mode() ? SwooleG.process_id : fd % reactor_num; + connection->reactor_id = reactor_id; connection->server_fd = (sw_atomic_t) server_fd; connection->last_recv_time = connection->connect_time = microtime(); connection->active = 1; + connection->worker_id = -1; connection->socket_type = ls->type; connection->socket = _socket; @@ -1682,28 +1992,29 @@ Connection *Server::add_connection(ListenPort *ls, Socket *_socket, int server_f _socket->direct_send = 1; } - sw_spinlock(&gs->spinlock); - SessionId session_id = gs->session_round; - // get session id - SW_LOOP_N(max_connection) { - Session *session = get_session(++session_id); - // available slot - if (session->fd == 0) { - session->fd = fd; - session->id = session_id; - session->reactor_id = connection->reactor_id; - break; - } + 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); } - gs->session_round = session_id; - sw_spinlock_release(&gs->spinlock); - connection->session_id = session_id; 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; @@ -1713,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() { @@ -1744,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++; } } @@ -1753,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 be8ec46446a..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 | +----------------------------------------------------------------------+ */ @@ -22,8 +22,8 @@ #include "swoole_redis.h" using swoole::http_server::Request; -using swoole::network::Socket; using swoole::network::Address; +using swoole::network::Socket; namespace swoole { @@ -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; @@ -81,7 +80,7 @@ static bool ssl_matches_wildcard_name(const char *subjectname, const char *certn * 3) no . between prefix and suffix **/ return strcasecmp(wildcard + 1, subjectname + subject_len - suffix_len) == 0 && - memchr(subjectname + prefix_len, '.', subject_len - suffix_len - prefix_len) == NULL; + memchr(subjectname + prefix_len, '.', subject_len - suffix_len - prefix_len) == NULL; } return 0; @@ -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,18 @@ 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 + +#ifdef SO_ACCEPTFILTER + if (tcp_defer_accept) { + struct accept_filter_arg a; + memset(&a, 0, sizeof(a)); + strcpy(a.af_name, "httpready"); + if (socket->set_option(SOL_SOCKET, SO_ACCEPTFILTER, &a, sizeof(a)) != 0) { + swoole_sys_warning("setsockopt(SO_ACCEPTFILTER) failed"); } } #endif @@ -169,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 @@ -177,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 } @@ -221,27 +231,24 @@ 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 = swHttp2_get_frame_length; + 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); - ls->protocol.get_package_length = swWebSocket_get_package_length; - ls->protocol.onPackage = swWebSocket_dispatch_frame; + } 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; } ls->protocol.package_length_offset = 0; 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) { @@ -260,14 +267,14 @@ bool ListenPort::import(int sock) { socket = new Socket(); socket->fd = 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; } @@ -288,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; } @@ -300,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) { @@ -308,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; @@ -324,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); } } @@ -341,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); } @@ -368,21 +376,20 @@ 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->websocket_status >= websocket::STATUS_HANDSHAKE) { if (conn->http_upgrade == 0) { serv->destroy_http_request(conn); - conn->websocket_status = WEBSOCKET_STATUS_ACTIVE; + conn->websocket_status = websocket::STATUS_ACTIVE; conn->http_upgrade = 1; } 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; @@ -407,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; @@ -422,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); @@ -465,18 +466,15 @@ 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; - swHttp2_send_setting_frame(protocol, _socket); + http2::send_setting_frame(protocol, _socket); if (buffer->length == sizeof(SW_HTTP2_PRI_STRING) - 1) { serv->destroy_http_request(conn); buffer->clear(); @@ -486,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 @@ -506,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); @@ -529,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; @@ -560,7 +593,7 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { } request_length = request->header_length_ + request->content_length_; if (request_length > protocol->package_max_length) { - swoole_error_log(SW_LOG_TRACE, + swoole_error_log(SW_LOG_WARNING, SW_ERROR_HTTP_INVALID_PROTOCOL, "Request Entity Too Large: request length (chunked) has already been greater than the " "package_max_length(%u)" CLIENT_INFO_FMT, @@ -578,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_TRACE, + 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_, @@ -603,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; @@ -618,14 +654,19 @@ static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) { if (buffer->length > request_length) { swoole_error_log(SW_LOG_TRACE, SW_ERROR_HTTP_INVALID_PROTOCOL, - "Invalid Request: %zu bytes has been disacard" CLIENT_INFO_FMT, + "Invalid Request: %zu bytes has been discard" CLIENT_INFO_FMT, buffer->length - request_length, CLIENT_INFO_ARGS); buffer->length = request_length; } 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); @@ -652,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); } @@ -712,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 f367d58a58f..3e677260c0b 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) { + 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) { - Worker *worker = &server_->workers[i]; - server_->destroy_worker(worker); + 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); } - if (send_buffer) { - sw_free(send_buffer); + + 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 (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,14 +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 */ @@ -154,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; } } @@ -168,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. @@ -186,93 +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 send_n = 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); - - if (send_n <= max_length) { - resp->info.flags = 0; - resp->info.len = send_n; - - iov[0].iov_base = &resp->info; - iov[0].iov_len = sizeof(resp->info); - iov[1].iov_base = (void *) resp->data; - iov[1].iov_len = send_n; - - int retval = _send(serv, &resp->info, iov, 2, 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; - resp->info.len = send_n; - - while (send_n > 0) { - if (send_n > max_length) { - copy_n = max_length; - } else { - resp->info.flags |= SW_EVENT_DATA_END; - copy_n = send_n; - } - - 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; - } - - send_n -= copy_n; - offset += copy_n; + if (server_->is_reactor_thread() || server_->single_thread) { + mb = &server_->get_thread(swoole_get_thread_id())->message_bus; + sock = mb->get_pipe_socket(worker->pipe_master); + } else { + mb = &server_->message_bus; + sock = worker->pipe_master; } - return 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(); } } @@ -301,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 { @@ -322,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 (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) { @@ -361,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. @@ -373,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) { +_close: + 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 78cf3d9caeb..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,64 +20,36 @@ 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 } } } ProcessPool *pool = &gs->event_workers; - if (ProcessPool::create(pool, worker_num, 0, SW_IPC_UNIXSOCK) < 0) { + *pool = {}; + if (pool->create(worker_num, 0, SW_IPC_UNIXSOCK) < 0) { return SW_ERR; } pool->set_max_request(max_request, max_request_grace); @@ -88,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; @@ -97,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(task.info.fd, Server::CLOSE_ACTIVELY); + } + case SW_SERVER_EVENT_CLOSE: { + factory->end(pipe_buffer->info.fd, Server::CLOSE_ACTIVELY); + break; + } + 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; @@ -290,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; } @@ -312,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 @@ -346,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(); } } @@ -355,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 @@ -395,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; } @@ -423,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 { @@ -436,16 +315,10 @@ static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode) { Event notify_ev{}; double now = microtime(); - if (now < serv->heartbeat_check_lasttime + 10) { - return; - } - notify_ev.type = SW_FD_SESSION; - int checktime = now - serv->heartbeat_idle_time; - - serv->foreach_connection([serv, checktime, reactor, ¬ify_ev](Connection *conn) { - if (conn->protect || conn->last_recv_time > checktime) { + serv->foreach_connection([serv, reactor, now, ¬ify_ev](Connection *conn) { + if (serv->is_healthy_connection(now, conn)) { return; } #ifdef SW_USE_OPENSSL @@ -465,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 43408acbe4b..2c1a5e9ae66 100644 --- a/src/server/reactor_thread.cc +++ b/src/server/reactor_thread.cc @@ -10,17 +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_server.h" #include "swoole_memory.h" #include "swoole_hash.h" -#include "swoole_http.h" -#include "swoole_client.h" #include "swoole_util.h" -#include "swoole_websocket.h" #include @@ -29,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; } @@ -90,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); @@ -143,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; } } @@ -174,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; } @@ -218,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) { @@ -242,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); } @@ -258,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 @@ -273,9 +246,9 @@ 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(); + for (; !serv->is_valid_connection(serv->get_connection(find_max_fd)) && find_max_fd > serv->get_minfd(); find_max_fd--) { // pass } @@ -303,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) { @@ -328,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) { @@ -337,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; @@ -351,112 +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(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_ERR; - } - - if (serv->disable_notify || conn->close_force) { - return Server::close_connection(reactor, conn->socket); - } - - 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. */ @@ -468,9 +435,9 @@ 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 active close, discard data. + // server actively closed connection, should discard the data if (Server::is_stream_event(send_data->info.type)) { // send_data->info.fd is session_id Connection *conn = serv->get_connection_verify(send_data->info.fd); @@ -495,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(); } @@ -505,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); } } @@ -524,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() @@ -560,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) { @@ -586,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); } @@ -617,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 @@ -640,9 +612,7 @@ static int ReactorThread_onWrite(Reactor *reactor, Event *ev) { while (!Buffer::empty(socket->out_buffer)) { BufferChunk *chunk = socket->out_buffer->front(); if (chunk->type == BufferChunk::TYPE_CLOSE) { - _close_fd: - reactor->close(reactor, socket); - return SW_OK; + return reactor->close(reactor, socket); } else if (chunk->type == BufferChunk::TYPE_SENDFILE) { ret = socket->handle_sendfile(); } else { @@ -655,7 +625,7 @@ static int ReactorThread_onWrite(Reactor *reactor, Event *ev) { if (ret < 0) { if (socket->close_wait) { conn->close_errno = errno; - goto _close_fd; + return reactor->trigger_close_event(ev); } else if (socket->send_wait) { break; } @@ -686,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] */ @@ -711,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; @@ -731,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; } /** @@ -754,77 +695,32 @@ 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: /** * heartbeat thread */ - if (heartbeat_check_interval >= 1 && heartbeat_check_interval <= heartbeat_idle_time) { - swTrace("hb timer start, time: %d live time:%d", heartbeat_check_interval, heartbeat_idle_time); + if (heartbeat_check_interval >= 1) { 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 reactor->event_num == thread->pipe_num; - }); - reactor->default_error_handler = ReactorThread_onClose; reactor->set_handler(SW_FD_PIPE | SW_EVENT_READ, ReactorThread_onPipeRead); @@ -857,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) { @@ -958,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; } @@ -971,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; @@ -1056,41 +933,35 @@ 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 checktime = microtime() - heartbeat_idle_time; - foreach_connection([this, checktime](Connection *conn) { - if (conn->protect || conn->last_recv_time == 0 || conn->last_recv_time > checktime) { + double now = microtime(); + foreach_connection([this, now](Connection *conn) { + SessionId session_id = conn->session_id; + if (session_id <= 0) { + return; + } + if (is_healthy_connection(now, conn)) { return; } DataHead ev{}; 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 = conn->session_id; - Socket *_pipe_sock = get_reactor_thread_pipe(conn->session_id, conn->reactor_id); - _pipe_sock->send_blocking((void *) &ev, sizeof(ev)); + ev.fd = session_id; + 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 08ac1d748b0..424fbd992b9 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -10,12 +10,11 @@ | to obtain it through the world-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_util.h" namespace swoole { using network::Socket; @@ -23,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 @@ -40,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; } @@ -79,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)) { @@ -107,43 +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; - - if (serv->is_base_mode()) { - serv->close_port(true); - } + 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; @@ -153,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); } /** @@ -183,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; } } @@ -208,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; @@ -227,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 task/finish, 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("task/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("task->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; @@ -290,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; } @@ -340,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("TaskWorker: 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("TaskWorker: 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 774b02d2eb8..0f6ca46074e 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -10,11 +10,10 @@ | to obtain it through the world-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 #include #include @@ -23,72 +22,58 @@ #include "swoole_server.h" #include "swoole_memory.h" #include "swoole_msg_queue.h" -#include "swoole_client.h" -#include "swoole_async.h" - -swoole::WorkerGlobal SwooleWG = {}; +#include "swoole_coroutine.h" 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 (SwooleTG.reactor) { - sw_server()->stop_async_worker(SwooleWG.worker); - } - /** - * Task worker - */ - else { - SwooleWG.shutdown = true; + if (swoole_event_is_available()) { + // Event Worker + sw_server()->stop_async_worker(sw_worker()); + } else { + // Task Worker + sw_worker()->shutdown(); } break; - /** - * for test - */ + // for test case SIGVTALRM: - swWarn("SIGVTALRM coming"); + swoole_warning("SIGVTALRM coming"); break; case SIGUSR1: case SIGUSR2: - sw_logger()->reopen(); + if (sw_logger()) { + sw_logger()->reopen(); + } break; default: #ifdef SIGRTMIN - if (signo == SIGRTMIN) { + if (signo == SIGRTMIN && sw_logger()) { sw_logger()->reopen(); } #endif @@ -96,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; @@ -109,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(serv, 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(this, 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(); @@ -327,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(); } } @@ -371,52 +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); + } + if (isset_hook(HOOK_WORKER_START)) { + call_hook(Server::HOOK_WORKER_START, hook_args); } -#endif - call_worker_start_callback(SwooleWG.worker); + swoole_clear_last_error(); + swoole_clear_last_error_msg(); + + 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 (!get_worker_message_bus()->empty()) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, "unprocessed data in the worker process buffer"); + get_worker_message_bus()->clear(); + } + + SwooleWG.running = false; + if (SwooleWG.worker_copy) { + delete SwooleWG.worker_copy; + SwooleWG.worker_copy = nullptr; + SwooleWG.worker = nullptr; } - if (worker_input_buffers) { - free_buffers(this, get_worker_buffer_num(), worker_input_buffers); +} + +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; } @@ -427,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); } @@ -456,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); @@ -474,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 { @@ -492,19 +466,17 @@ 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; } - int remaining_time = serv->max_wait_time - (time(nullptr) - SwooleWG.exit_time); + int remaining_time = serv->max_wait_time - (::time(nullptr) - SwooleWG.exit_time); if (remaining_time <= 0) { 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) { @@ -530,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 @@ -562,19 +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; + 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,23 +579,14 @@ 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); - if (SwooleTG.reactor) { - return SwooleTG.reactor->write(SwooleTG.reactor, pipe_sock, ev_data, sendn); + 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 { return pipe_sock->send_blocking(ev_data, sendn); } } -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 (SwooleTG.reactor) { - return swoole_event_writev(pipe_sock, iov, iovcnt); - } else { - return pipe_sock->writev_blocking(iov, iovcnt); - } -} - /** * send message from worker to another worker */ @@ -624,104 +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]; - void *buffer; - struct iovec buffers[2]; - int recv_chunk_count = 0; - -_read_from_pipe: - recv_n = recv(event->fd, &pipe_buffer->info, sizeof(pipe_buffer->info), MSG_PEEK); - if (recv_n < 0) { - if (errno == EAGAIN) { - 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) { - buffer = serv->get_buffer(serv, &pipe_buffer->info); - size_t remain_len = pipe_buffer->info.len - serv->get_buffer_len(serv, &pipe_buffer->info); - - buffers[0].iov_base = &pipe_buffer->info; - buffers[0].iov_len = sizeof(pipe_buffer->info); - buffers[1].iov_base = buffer; - buffers[1].iov_len = SW_MIN(serv->ipc_max_size - sizeof(pipe_buffer->info), remain_len); - - recv_n = readv(event->fd, buffers, 2); - assert(recv_n != 0); - if (recv_n < 0 && errno == EAGAIN) { - return SW_OK; - } - if (recv_n > 0) { - serv->add_buffer_len(serv, &pipe_buffer->info, recv_n - sizeof(pipe_buffer->info)); - } - - 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 { - pipe_buffer->info.flags |= SW_EVENT_DATA_OBJ_PTR; - /** - * Because we don't want to split the EventData parameters into swDataHead 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. - */ - serv->move_buffer(serv, pipe_buffer); - } - } else { - recv_n = event->socket->read(pipe_buffer, serv->ipc_max_size); + if (serv->get_worker_message_bus()->read(event->socket) <= 0) { + return SW_OK; } - if (recv_n > 0) { - return serv->accept_task((EventData *) pipe_buffer); - } + serv->worker_accept_event(&pipe_buffer->info); + serv->get_worker_message_bus()->pop(); - return SW_ERR; + return SW_OK; } -ssize_t Worker::send_pipe_message(const void *buf, size_t n, int flags) { - Socket *pipe_sock; - - if (flags & SW_PIPE_MASTER) { - pipe_sock = pipe_master; - } else { - pipe_sock = pipe_worker; - } - - // message-queue - if (pool->use_msgqueue) { - struct { - long mtype; - EventData buf; - } msg; - - msg.mtype = id + 1; - memcpy(&msg.buf, buf, n); - - return pool->queue->push((QueueNode *) &msg, n) ? n : -1; - } - - if ((flags & SW_PIPE_NONBLOCK) && SwooleTG.reactor) { - return SwooleTG.reactor->write(SwooleTG.reactor, pipe_sock, buf, n); - } else { - return pipe_sock->send_blocking(buf, n); - } -} } // namespace swoole diff --git a/src/wrapper/event.cc b/src/wrapper/event.cc index 31b8e699295..8598a4bcca6 100644 --- a/src/wrapper/event.cc +++ b/src/wrapper/event.cc @@ -10,21 +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 | +----------------------------------------------------------------------+ */ #include "swoole_api.h" -#include "swoole_socket.h" #include "swoole_reactor.h" #include "swoole_client.h" -#include "swoole_async.h" -#include "swoole_coroutine_c_api.h" #include "swoole_coroutine_socket.h" #include "swoole_coroutine_system.h" #include -#include using namespace swoole; @@ -66,6 +62,17 @@ int swoole_event_add(Socket *socket, int events) { return SwooleTG.reactor->add(socket, events); } +int swoole_event_add_or_update(swoole::network::Socket *_socket, int event) { + if (event == SW_EVENT_READ) { + return SwooleTG.reactor->add_read_event(_socket); + } else if (event == SW_EVENT_WRITE) { + return SwooleTG.reactor->add_write_event(_socket); + } else { + assert(0); + return SW_ERR; + } +} + int swoole_event_set(Socket *socket, int events) { return SwooleTG.reactor->set(socket, events); } @@ -78,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; @@ -97,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); } @@ -119,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 eba0c951310..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 | +----------------------------------------------------------------------+ */ @@ -25,8 +25,20 @@ Timer *sw_timer() { } #endif +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(SwooleTG.timer == nullptr)) { + if (sw_unlikely(!swoole_timer_is_available())) { SwooleTG.timer = new Timer(); if (sw_unlikely(!SwooleTG.timer->init())) { delete SwooleTG.timer; @@ -38,16 +50,24 @@ TimerNode *swoole_timer_add(long ms, bool persistent, const TimerCallback &callb } bool swoole_timer_del(TimerNode *tnode) { + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); + return false; + } return SwooleTG.timer->remove(tnode); } void swoole_timer_delay(TimerNode *tnode, long delay_ms) { + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); + return; + } return SwooleTG.timer->delay(tnode, 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); @@ -60,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); @@ -72,8 +92,8 @@ long swoole_timer_tick(long ms, const TimerCallback &callback, void *private_dat } bool swoole_timer_exists(long timer_id) { - if (!SwooleTG.timer) { - swWarn("no timer"); + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); return false; } TimerNode *tnode = SwooleTG.timer->get(timer_id); @@ -81,28 +101,32 @@ bool swoole_timer_exists(long timer_id) { } bool swoole_timer_clear(long timer_id) { + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); + return false; + } return SwooleTG.timer->remove(SwooleTG.timer->get(timer_id)); } TimerNode *swoole_timer_get(long timer_id) { - if (!SwooleTG.timer) { - swWarn("no timer"); + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); return nullptr; } return SwooleTG.timer->get(timer_id); } void swoole_timer_free() { - if (!SwooleTG.timer) { + if (!swoole_timer_is_available()) { + swoole_warning("timer is not available"); return; } delete SwooleTG.timer; SwooleTG.timer = nullptr; - SwooleG.signal_alarm = false; } int swoole_timer_select() { - if (!SwooleTG.timer) { + if (!swoole_timer_is_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 new file mode 100644 index 00000000000..30d457e8d8c --- /dev/null +++ b/tests/include/api/curl_multi.php @@ -0,0 +1,70 @@ += 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/A.php b/tests/include/api/test_classes/A.php new file mode 100644 index 00000000000..5b672df8735 --- /dev/null +++ b/tests/include/api/test_classes/A.php @@ -0,0 +1,4 @@ + 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 275806ae0cf..ffd8dd685d5 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -11,20 +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('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/'); @@ -48,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])) { @@ -73,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); @@ -91,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); @@ -121,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 e8b615fc711..8efafaaed93 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -19,6 +19,10 @@ function clear_php() `ps -A | grep php | grep -v phpstorm | grep -v 'run-tests' | awk '{print $1}' | xargs kill -9 > /dev/null 2>&1`; } +function puts($msg) { + echo $msg."\n"; +} + function top(int $pid) { static $available; @@ -84,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(); @@ -144,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; + } } } } @@ -160,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(); @@ -359,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); @@ -436,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(); } @@ -493,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 { @@ -611,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; }; } @@ -625,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) { @@ -642,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]; } /** @@ -654,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]; @@ -761,4 +791,84 @@ function dump_to_file($file, $data) fwrite($fp, $l . "\n"); } fclose($fp); -} \ No newline at end of file +} + +function curl_type_assert($ch, $resource_type, $class_type) +{ + if (PHP_VERSION_ID >= 80000) { + Assert::isInstanceOf($ch, $class_type); + } else { + Assert::eq(get_resource_type($ch), $resource_type); + } +} + +function swoole_get_variance($avg, $array, $is_swatch = false) +{ + $count = count($array); + if ($count == 1 && $is_swatch == true) { + return false; + } elseif ($count > 0) { + $total_var = 0; + foreach ($array as $lv) { + $total_var += pow(($lv - $avg), 2); + } + if ($count == 1 && $is_swatch == true) { + return false; + } + return $is_swatch ? sqrt($total_var / (count($array) - 1)) : sqrt($total_var / count($array)); + } else { + return false; + } +} + +function swoole_get_average($array) +{ + return array_sum($array) / count($array); +} + +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 new file mode 100644 index 00000000000..634505deba8 --- /dev/null +++ b/tests/include/lib/composer.json @@ -0,0 +1,36 @@ +{ + "name": "swoole_tests/lib", + "description": "for swoole tests", + "authors": [ + { + "name": "tianfeng.han", + "email": "rango@swoole.com" + } + ], + "autoload": { + "psr-4": { + "SwooleTest\\": "src" + } + }, + "require": { + "guzzlehttp/guzzle": "^7.3", + "guzzlehttp/promises": "^1.4", + "guzzlehttp/psr7": "^1.7", + "php-http/async-client-implementation": "^1.0", + "php-http/client-common": "^1.5|^2.0", + "php-http/discovery": "^1.6.1", + "php-http/httplug": "^1.1|^2.0", + "php-http/message": "^1.5", + "psr/http-factory": "^1.0", + "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/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 d029825a6ff..acf69a63b9e 100644 --- a/tests/include/lib/src/CurlManager.php +++ b/tests/include/lib/src/CurlManager.php @@ -9,6 +9,15 @@ class CurlManager { protected $port; + protected $nativeCurl = false; + + function __construct() { + $this->nativeCurl = defined('SWOOLE_HOOK_NATIVE_CURL'); + } + + function disableNativeCurl() { + $this->nativeCurl = false; + } function getUrlBase() { @@ -43,7 +52,8 @@ function run(callable $fn, $createCliServer = true) global $argc, $argv; if (!($argc > 1 and $argv[1] == 'ori')) { - Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_CURL); + $flags = $this->nativeCurl ? SWOOLE_HOOK_NATIVE_CURL : SWOOLE_HOOK_CURL; + Swoole\Runtime::enableCoroutine($flags); } run(function () use ($fn, $proc) { @@ -57,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 72c7bac719b..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 | +----------------------------------------------------------------------+ */ @@ -31,6 +31,8 @@ class ProcessManager */ protected $atomic; protected $alone = false; + protected $onlyChild = false; + protected $onlyParent = false; protected $freePorts = []; protected $randomFunc = 'get_safe_random'; protected $randomData = [[]]; @@ -47,9 +49,10 @@ class ProcessManager public $useConstantPorts = false; protected $childPid; - protected $childStatus = 255; + protected $childExitStatus = 255; protected $expectExitSignal = [0, SIGTERM]; protected $parentFirst = false; + protected $killed = false; /** * @var Process */ @@ -238,7 +241,8 @@ public function kill(bool $force = false) if (!defined('PCNTL_ESRCH')) { define('PCNTL_ESRCH', 3); } - if (!$this->alone && $this->childPid) { + if (!$this->alone and !$this->killed and $this->childPid) { + $this->killed = true; if ($force || (!@Process::kill($this->childPid) && swoole_errno() !== PCNTL_ESRCH)) { if (!@Process::kill($this->childPid, SIGKILL) && swoole_errno() !== PCNTL_ESRCH) { exit('KILL CHILD PROCESS ERROR'); @@ -247,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(); @@ -269,14 +278,23 @@ public function run($redirectStdout = false) $this->alone = true; $this->initFreePorts(); if ($argv[1] == 'child') { - return $this->runChildFunc(); + $this->onlyChild = true; } elseif ($argv[1] == 'parent') { - return $this->runParentFunc(); + $this->onlyParent = true; } else { throw new RuntimeException("bad parameter \$1\n"); } } $this->initFreePorts(); + if ($this->alone) { + if ($this->onlyChild) { + return $this->runChildFunc(); + } elseif ($this->onlyParent) { + return $this->runParentFunc(); + } + $this->alone = false; + } + $this->childProcess = new Process(function () { if ($this->parentFirst) { $this->wait(); @@ -296,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']}"); } @@ -324,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)) { @@ -336,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 5a78508c774..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/lib/vendor/autoload.php b/tests/include/lib/vendor/autoload.php deleted file mode 100755 index e5526feda7b..00000000000 --- a/tests/include/lib/vendor/autoload.php +++ /dev/null @@ -1,11 +0,0 @@ -=')) { + skip('need php version >= ' . $require_version); + } +} + function skip_if_php_version_between($a, $b) { if (version_compare(PHP_VERSION, $a, '>=') && version_compare(PHP_VERSION, $b, '<=')) { @@ -76,7 +83,14 @@ function skip_if_ini_bool_equal_to(string $name, bool $value) } } -function skip_if_constant_not_defined(string $constant_name) +function skip_if_no_nghttp(): void +{ + if (!str_contains(`nghttp --version 2>&1`, 'nghttp2')) { + skip('no nghttp'); + } +} + +function skip_if_constant_not_defined(string $constant_name): void { require_once __DIR__ . '/config.php'; skip("{$constant_name} is not defined", !defined($constant_name)); @@ -147,6 +161,16 @@ function skip_if_darwin() skip('not support on darwin', stripos(PHP_OS, 'Darwin') !== false); } +function skip_if_nts() +{ + skip('not support in nts', !defined('SWOOLE_THREAD')); +} + +function skip_if_not_linux() +{ + skip('only support linux', PHP_OS !== 'Linux'); +} + function skip_if_musl_libc() { skip('not support when use musl libc', !empty(`ldd 2>&1 | grep -i musl`)); @@ -162,9 +186,9 @@ function skip_if_in_valgrind(string $reason = 'valgrind is too slow') skip($reason, getenv('USE_ZEND_ALLOC') === '0'); } -function skip_if_in_travis(string $reason = 'not support in travis') +function skip_if_in_ci(string $reason = 'not support in CI') { - skip($reason, file_exists('/.travisenv')); + skip($reason, file_exists('/.cienv')); } function skip_if_in_docker(string $reason = 'not support in docker') @@ -257,3 +281,23 @@ function skip_unsupported(string $message = '') { skip($message ?: 'the test cannot continue to work for some implementation reasons'); } + +function skip_if_no_coroutine_get_execute_time() +{ + skip('no Swoole\Coroutine::getExecuteTime', !method_exists(Swoole\Coroutine::class, 'getExecuteTime')); +} + +function skip_if_no_ftp() +{ + require_once __DIR__ . '/config.php'; + skip('no available proxy', !check_tcp_port(FTP_HOST, FTP_PORT)); +} + +function skip_if_not_root() +{ + skip('not root user', posix_geteuid() !== 0); +} + +function skip_if_no_iouring() { + skip('no io-uring supports', !defined('SWOOLE_IOURING_DEFAULT')); +} diff --git a/tests/include/ssl_certs/mosquitto.org.crt b/tests/include/ssl_certs/mosquitto.org.crt new file mode 100644 index 00000000000..e76dbd85598 --- /dev/null +++ b/tests/include/ssl_certs/mosquitto.org.crt @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL +BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG +A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU +BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv +by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE +BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES +MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp +dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg +UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW +Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA +s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH +3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo +E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT +MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV +6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC +6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf ++pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK +sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839 +LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE +m/XriWr/Cq4h/JfB7NTsezVslgkBaoU= +-----END CERTIFICATE----- diff --git a/tests/init b/tests/init index 15fb1006c19..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'; -go(function () { +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 @@ go(function () { exit(1); } } - echo "[DB-init] Done!\n"; -}); \ No newline at end of file + 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/discard.phpt b/tests/swoole_channel_coro/discard.phpt index 104701d24ae..03c54cfbe6f 100644 --- a/tests/swoole_channel_coro/discard.phpt +++ b/tests/swoole_channel_coro/discard.phpt @@ -23,4 +23,4 @@ var_dump('scheduler end'); --EXPECTF-- string(12) "push success" string(13) "scheduler end" -[%s] WARNING ~Channel (ERRNO 10003): channel is destroyed, 1 producers will be discarded +[%s] WARNING Channel::~Channel() (ERRNO 10003): channel is destroyed, 1 producers will be discarded 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/enableSSL.phpt b/tests/swoole_client_coro/enableSSL.phpt new file mode 100644 index 00000000000..4e022ccdd63 --- /dev/null +++ b/tests/swoole_client_coro/enableSSL.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_client_coro: enableSSL +--SKIPIF-- + +--FILE-- +connect('www.baidu.com', 443); + $client->enableSSL(); + + $http = "GET / HTTP/1.0\r\nAccept: */*User-Agent: Lowell-Agent\r\nHost: www.baidu.com\r\nConnection: Close\r\n\r\n"; + if (!$client->send($http)) { + echo "ERROR\n"; + } + + $content = ''; + while (true) { + $read = $client->recv(); + if (empty($read)) { + break; + } + $content .= $read; + } + $client->close(); + Assert::assert(strpos($content, 'map.baidu.com') !== false); +}); +?> +--EXPECT-- 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/sendto.phpt b/tests/swoole_client_coro/sendto.phpt index 3b2fa2b8565..024d2c25399 100644 --- a/tests/swoole_client_coro/sendto.phpt +++ b/tests/swoole_client_coro/sendto.phpt @@ -1,7 +1,8 @@ --TEST-- swoole_client_coro: sendto --SKIPIF-- - + --FILE-- +--FILE-- + TEST_LOG_FILE]); + +Coroutine\run(function () { + $client = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL); + $client->set([ + 'ssl_cafile' => SSL_FILE_DIR . '/mosquitto.org.crt', + 'ssl_verify_peer' => true, + ]); + $ret = $client->connect('test.mosquitto.org', 8883); + Assert::true($ret); + + $client2 = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP | SWOOLE_SSL); + $client2->set([ + 'ssl_cafile' => SSL_FILE_DIR . '/mosquitto.org.crt', + 'ssl_verify_peer' => true, + ]); + $ret = $client2->connect('baidu.com', 443); + Assert::false($ret); + Assert::eq($client2->errCode, SWOOLE_ERROR_SSL_VERIFY_FAILED); +}); + +?> +--EXPECT-- 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/sendto.phpt b/tests/swoole_client_sync/sendto.phpt index 6159be9bbf2..db1bef07743 100644 --- a/tests/swoole_client_sync/sendto.phpt +++ b/tests/swoole_client_sync/sendto.phpt @@ -1,7 +1,10 @@ --TEST-- swoole_client_sync: sendto --SKIPIF-- - + --FILE-- parentFunc = function () use ($pm) { $cli = new Client(SWOOLE_SOCK_UDP); - $cli->sendto('127.0.0.1', $pm->getFreePort(), "hello"); - $cli->sendto('localhost', $pm->getFreePort(), "hello"); + Assert::true($cli->sendto('127.0.0.1', $pm->getFreePort(), "packet-1")); + Assert::true($cli->sendto('localhost', $pm->getFreePort(), "packet-2")); Assert::false($cli->sendto('error_domain', $pm->getFreePort(), "hello")); Assert::assert($cli->errCode, SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED); - $cli->sendto('localhost', $pm->getFreePort(), "hello"); + Assert::true($cli->sendto('localhost', $pm->getFreePort(), "packet-3")); echo "DONE\n"; }; $pm->childFunc = function () use ($pm) { @@ -29,11 +32,11 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $peer = null; $ret = $socket->recvfrom($peer); - Assert::eq($ret, 'hello', 'packet-1'); + Assert::eq($ret, 'packet-1'); $ret = $socket->recvfrom($peer); - Assert::eq($ret, 'hello', 'packet-2'); + Assert::eq($ret, 'packet-2'); $ret = $socket->recvfrom($peer); - Assert::eq($ret, 'hello', 'packet-3'); + Assert::eq($ret, 'packet-3'); }); }; $pm->childFirst(); 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 1c3cf8d560a..b3f9f591866 100644 --- a/tests/swoole_coroutine/bailout/error.phpt +++ b/tests/swoole_coroutine/bailout/error.phpt @@ -5,16 +5,20 @@ swoole_coroutine/bailout: error --FILE-- --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 891889fbd5a..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 +[%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/channel_pop.phpt b/tests/swoole_coroutine/cancel/channel_pop.phpt new file mode 100644 index 00000000000..a5c539391ca --- /dev/null +++ b/tests/swoole_coroutine/cancel/channel_pop.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_coroutine/cancel: pop from channel +--SKIPIF-- + +--FILE-- +pop(100), false); + Assert::assert(Coroutine::isCanceled()); + Assert::eq($chan->errCode, SWOOLE_CHANNEL_CANCELED); + echo "Done\n"; +}); + +?> +--EXPECT-- +Done diff --git a/tests/swoole_coroutine/cancel/channel_push.phpt b/tests/swoole_coroutine/cancel/channel_push.phpt new file mode 100644 index 00000000000..95b6d40b171 --- /dev/null +++ b/tests/swoole_coroutine/cancel/channel_push.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_coroutine/cancel: push to channel +--SKIPIF-- + +--FILE-- +push("hello world [1]", 100)); + Assert::eq(Coroutine::isCanceled(), false); + Assert::eq($chan->errCode, SWOOLE_CHANNEL_OK); + + Assert::eq($chan->push("hello world [2]", 100), false); + Assert::eq(Coroutine::isCanceled(), true); + Assert::eq($chan->errCode, SWOOLE_CHANNEL_CANCELED); + + echo "Done\n"; +}); + +?> +--EXPECT-- +Done diff --git a/tests/swoole_coroutine/cancel/error.phpt b/tests/swoole_coroutine/cancel/error.phpt new file mode 100644 index 00000000000..6ad96775168 --- /dev/null +++ b/tests/swoole_coroutine/cancel/error.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_coroutine/cancel: error +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_coroutine/cancel/gethostbyname.phpt b/tests/swoole_coroutine/cancel/gethostbyname.phpt new file mode 100644 index 00000000000..d3029cb26d0 --- /dev/null +++ b/tests/swoole_coroutine/cancel/gethostbyname.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_coroutine/cancel: gethostbyname +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_coroutine/cancel/sleep.phpt b/tests/swoole_coroutine/cancel/sleep.phpt new file mode 100644 index 00000000000..e7fa72dd1a9 --- /dev/null +++ b/tests/swoole_coroutine/cancel/sleep.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_coroutine/cancel: sleep +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_coroutine/cancel/socket.phpt b/tests/swoole_coroutine/cancel/socket.phpt new file mode 100644 index 00000000000..0d365b735aa --- /dev/null +++ b/tests/swoole_coroutine/cancel/socket.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_coroutine/cancel: socket +--SKIPIF-- + +--FILE-- +bind('127.0.0.1', 9601); + // server + $cid = go(function () use ($socket) { + while (true) { + $peer = null; + $data = $socket->recvfrom($peer); + Assert::assert(empty($data)); + Assert::assert($socket->errCode == SOCKET_ECANCELED); + break; + } + echo "DONE\n"; + }); + + // client + co::sleep(0.1); + Assert::true(Coroutine::cancel($cid)); +}); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_coroutine/cancel/suspend.phpt b/tests/swoole_coroutine/cancel/suspend.phpt new file mode 100644 index 00000000000..2f4945c7f59 --- /dev/null +++ b/tests/swoole_coroutine/cancel/suspend.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_coroutine/cancel: suspend +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_coroutine/cancel/wait.phpt b/tests/swoole_coroutine/cancel/wait.phpt new file mode 100644 index 00000000000..75b24b78723 --- /dev/null +++ b/tests/swoole_coroutine/cancel/wait.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_coroutine/cancel: wait/waitpid +--SKIPIF-- + +--FILE-- +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); +}); + +?> +--EXPECT-- +Done diff --git a/tests/swoole_coroutine/cancel/wait_event.phpt b/tests/swoole_coroutine/cancel/wait_event.phpt new file mode 100644 index 00000000000..ee412b62bad --- /dev/null +++ b/tests/swoole_coroutine/cancel/wait_event.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_coroutine/cancel: waitEvent +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_coroutine/cancel/wait_signal.phpt b/tests/swoole_coroutine/cancel/wait_signal.phpt new file mode 100644 index 00000000000..7631e30df42 --- /dev/null +++ b/tests/swoole_coroutine/cancel/wait_signal.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_coroutine/cancel: waitSignal +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_coroutine/check.phpt b/tests/swoole_coroutine/check.phpt index 4cb532f2d5a..ab166aaee93 100644 --- a/tests/swoole_coroutine/check.phpt +++ b/tests/swoole_coroutine/check.phpt @@ -5,6 +5,9 @@ swoole_coroutine: check if is in the coroutine --FILE-- 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 @@ -54,9 +45,9 @@ $map = [ Co::getaddrinfo('www.swoole.com'); Assert::assert(0); // never here }, - // function () { - // Co::statvfs(__DIR__); // can use outside the coroutine - // }, +// function () { +// Co::statvfs(__DIR__); +// }, function () { Co::exec('echo'); Assert::assert(0); // never here @@ -77,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(); @@ -112,6 +79,10 @@ if (class_exists(Co\Http2\Client::class)) { $info_list = []; foreach ($map as $i => $f) { $GLOBALS['f'] = $f; + if (RUN_IN_CHILD == false) { + $f(); + continue; + } $process = new Swoole\Process(function () { function a() { @@ -139,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_1.phpt b/tests/swoole_coroutine/dnslookup_1.phpt index 26af90ddb51..59e41155393 100644 --- a/tests/swoole_coroutine/dnslookup_1.phpt +++ b/tests/swoole_coroutine/dnslookup_1.phpt @@ -8,10 +8,12 @@ skip_if_offline(); --EXPECT-- diff --git a/tests/swoole_coroutine/dnslookup_2.phpt b/tests/swoole_coroutine/dnslookup_2.phpt index 9de32793040..f1727cdc712 100644 --- a/tests/swoole_coroutine/dnslookup_2.phpt +++ b/tests/swoole_coroutine/dnslookup_2.phpt @@ -5,12 +5,14 @@ swoole_coroutine: async dns lookup timeout --FILE-- '192.0.0.1:10053']); + +use Swoole\Coroutine\System; +use function Swoole\Coroutine\run; + +run(function () { $host = Swoole\Coroutine::dnsLookup('www.' . uniqid() . '.' . uniqid(), 0.5); Assert::eq($host, false); Assert::eq(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED); - Swoole\Event::exit(); }); ?> --EXPECT-- diff --git a/tests/swoole_coroutine/dnslookup_3.phpt b/tests/swoole_coroutine/dnslookup_3.phpt index 82a7ce6f5b7..bf1c23f3532 100644 --- a/tests/swoole_coroutine/dnslookup_3.phpt +++ b/tests/swoole_coroutine/dnslookup_3.phpt @@ -10,8 +10,7 @@ go(function () { $host = swoole_async_dns_lookup_coro('www.' . uniqid() . '.' . uniqid(), 15); Assert::eq($host, false); Assert::eq(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED); - Swoole\Event::exit(); }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_coroutine/dnslookup_4.phpt b/tests/swoole_coroutine/dnslookup_4.phpt new file mode 100644 index 00000000000..0830b6d8c6e --- /dev/null +++ b/tests/swoole_coroutine/dnslookup_4.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_coroutine: bad dns server +--SKIPIF-- + +--FILE-- + '192.0.0.1:10053']); + $host = Swoole\Coroutine::dnsLookup('www.baidu.com', 0.5); + Assert::eq($host, false); + Assert::eq(swoole_last_error(), SWOOLE_ERROR_DNSLOOKUP_RESOLVE_FAILED); +}); +?> +--EXPECT-- diff --git a/tests/swoole_coroutine/dnslookup_5.phpt b/tests/swoole_coroutine/dnslookup_5.phpt new file mode 100644 index 00000000000..7e602d2c163 --- /dev/null +++ b/tests/swoole_coroutine/dnslookup_5.phpt @@ -0,0 +1,18 @@ +--TEST-- +swoole_coroutine: async dns lookup [5] +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_coroutine/dnslookup_ipv6.phpt b/tests/swoole_coroutine/dnslookup_ipv6.phpt new file mode 100644 index 00000000000..af94e63859f --- /dev/null +++ b/tests/swoole_coroutine/dnslookup_ipv6.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_coroutine: dns Lookup IPv6 +--SKIPIF-- + +--FILE-- + +--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 8540fc621d1..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 +[%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 new file mode 100644 index 00000000000..472edc3f612 --- /dev/null +++ b/tests/swoole_coroutine/fatal_error.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_coroutine: fatal error +--SKIPIF-- + +--FILE-- + true, +]); + +register_shutdown_function(function () { + echo "shutdown\n"; +}); + +Co\run(function () { + test_not_found(); +}); +echo "DONE\n"; +?> +--EXPECTF-- +Fatal error: Uncaught Error: Call to undefined function test_not_found() in %s:%d +Stack trace: +%A + thrown in %s on line %d +shutdown diff --git a/tests/swoole_coroutine/gethostbyname.phpt b/tests/swoole_coroutine/gethostbyname.phpt index 3a3453112a6..655804d02f8 100644 --- a/tests/swoole_coroutine/gethostbyname.phpt +++ b/tests/swoole_coroutine/gethostbyname.phpt @@ -8,8 +8,14 @@ skip_if_offline(); --FILE-- null, 'www.youtube.com' => null, 'www.facebook.com' => null, @@ -22,7 +28,7 @@ Co\run(function () { $first_time = microtime(true); for ($n = MAX_CONCURRENCY_LOW; $n--;) { foreach ($map as $host => &$ip) { - $ip = co::gethostbyname($host); + $ip = System::gethostbyname($host); Assert::assert(preg_match(IP_REGEX, $ip)); } } @@ -33,7 +39,7 @@ Co\run(function () { $cache_time = microtime(true); for ($n = MAX_CONCURRENCY_LOW; $n--;) { foreach ($map as $host => $ip) { - $_ip = co::gethostbyname($host); + $_ip = System::gethostbyname($host); Assert::same($ip, $_ip); } } @@ -42,7 +48,7 @@ Co\run(function () { $no_cache_time = microtime(true); for ($n = MAX_CONCURRENCY_LOW; $n--;) { swoole_clear_dns_cache(); - $ip = co::gethostbyname(array_rand($map)); + $ip = System::gethostbyname(array_rand($map)); Assert::assert(preg_match(IP_REGEX, $ip)); } $no_cache_time = microtime(true) - $no_cache_time; @@ -52,7 +58,7 @@ Co\run(function () { for ($c = MAX_CONCURRENCY_LOW; $c--;) { go(function () use ($map, $chan) { swoole_clear_dns_cache(); - $ip = co::gethostbyname(array_rand($map)); + $ip = System::gethostbyname(array_rand($map)); Assert::assert(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)); $chan->push(Assert::assert(preg_match(IP_REGEX, $ip))); }); @@ -63,7 +69,7 @@ Co\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/kernel_coroutine.phpt b/tests/swoole_coroutine/kernel_coroutine.phpt new file mode 100644 index 00000000000..c4f128c4a1c --- /dev/null +++ b/tests/swoole_coroutine/kernel_coroutine.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_coroutine: kernel coroutine +--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/aio_thread_num.phpt b/tests/swoole_coroutine_system/aio_thread_num.phpt index 81b801ee5a3..83d97297751 100644 --- a/tests/swoole_coroutine_system/aio_thread_num.phpt +++ b/tests/swoole_coroutine_system/aio_thread_num.phpt @@ -7,14 +7,19 @@ skip_if_offline(); --FILE-- swoole_cpu_num(), 'aio_max_wait_time' => 0.0005 ]); -// init aio core workers -Co\run(function () { - Co::readFile(__FILE__); +run(function () { + System::readFile(__FILE__); }); $sch = new Swoole\Coroutine\Scheduler(); @@ -23,15 +28,15 @@ $sch->add(function () { static $worker_num = 0; $n = 100; while ($n--) { - $worker_num = max($worker_num, Co::stats()['aio_worker_num']); + $worker_num = max($worker_num, Coroutine::stats()['aio_worker_num']); Swoole\Coroutine\System::sleep(0.001); } Assert::greaterThan($worker_num, swoole_cpu_num()); phpt_var_dump($worker_num); }); $sch->parallel(swoole_cpu_num() * [4, 16, 32, 64][PRESSURE_LEVEL], function () { - Co::sleep(mt_rand(1, 5) / 1000); - $result = Swoole\Coroutine\System::gethostbyname('www.baidu.com'); + System::sleep(mt_rand(1, 5) / 1000); + $result = Swoole\Coroutine\System::getaddrinfo('www.baidu.com'); Assert::notEmpty($result); }); $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/1.phpt b/tests/swoole_curl/basic/1.phpt new file mode 100644 index 00000000000..878d22384b8 --- /dev/null +++ b/tests/swoole_curl/basic/1.phpt @@ -0,0 +1,41 @@ +--TEST-- +swoole_curl/basic: Test curl_exec() function with basic functionality +--CREDITS-- +Sebastian Deutsch + +TestFest 2009 - AFUP - Jean-Marc Fontaine + +--SKIPIF-- + +--FILE-- +run(function ($host) { + // start testing + echo "*** Testing curl_exec() : basic functionality ***\n"; + + $url = $host. "/get.php?test=get"; + $ch = curl_init(); + + ob_start(); // start output buffering + curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use + $ok = curl_exec($ch); + curl_close($ch); + $curl_content = ob_get_contents(); + ob_end_clean(); + + if ($ok) { + var_dump($curl_content); + } else { + echo "curl_exec returned false"; + } +}); + +?> +--EXPECTF-- +*** Testing curl_exec() : basic functionality *** +string(25) "Hello World! +Hello World!" diff --git a/tests/swoole_curl/basic/10.phpt b/tests/swoole_curl/basic/10.phpt new file mode 100644 index 00000000000..ed748798ce0 --- /dev/null +++ b/tests/swoole_curl/basic/10.phpt @@ -0,0 +1,33 @@ +--TEST-- +swoole_curl/basic: Test curl_error() & curl_errno() function with problematic proxy +--CREDITS-- +TestFest 2009 - AFUP - Perrick Penet +--SKIPIF-- + + +--FILE-- +run(function ($host) { + $url = "http://www.example.org"; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_PROXY, uniqid() . ":" . uniqid()); + curl_setopt($ch, CURLOPT_URL, $url); + + curl_exec($ch); + Assert::eq(curl_errno($ch), CURLE_COULDNT_RESOLVE_PROXY); + curl_close($ch); + echo "DONE\n"; +}, false); + +?> +--EXPECT-- +DONE diff --git a/tests/swoole_library/curl/basic/11.phpt b/tests/swoole_curl/basic/11.phpt similarity index 82% rename from tests/swoole_library/curl/basic/11.phpt rename to tests/swoole_curl/basic/11.phpt index dc32e7b6d4c..7c64ff0cf73 100644 --- a/tests/swoole_library/curl/basic/11.phpt +++ b/tests/swoole_curl/basic/11.phpt @@ -1,9 +1,9 @@ --TEST-- -swoole_library/curl/basic: Test curl_opt() function with COOKIE +swoole_curl/basic: Test curl_opt() function with COOKIE --CREDITS-- TestFest 2009 - AFUP - Xavier Gorse --SKIPIF-- - + --FILE-- * Alias to functions: */ -require __DIR__ . '/../../../include/bootstrap.php'; +require __DIR__ . '/../../include/bootstrap.php'; $cm = new \SwooleTest\CurlManager(); $cm->run(function ($host) { diff --git a/tests/swoole_library/curl/basic/12.phpt b/tests/swoole_curl/basic/12.phpt similarity index 81% rename from tests/swoole_library/curl/basic/12.phpt rename to tests/swoole_curl/basic/12.phpt index c1df4bf2dad..546dad2464f 100644 --- a/tests/swoole_library/curl/basic/12.phpt +++ b/tests/swoole_curl/basic/12.phpt @@ -1,9 +1,9 @@ --TEST-- -swoole_library/curl/basic: Test curl_opt() function with CURLOPT_HTTP_VERSION/CURL_HTTP_VERSION_1_0 +swoole_curl/basic: Test curl_opt() function with CURLOPT_HTTP_VERSION/CURL_HTTP_VERSION_1_0 --CREDITS-- TestFest 2009 - AFUP - Xavier Gorse --SKIPIF-- - + --FILE-- * Source code: ext/curl/interface.c * Alias to functions: */ -require __DIR__ . '/../../../include/bootstrap.php'; +require __DIR__ . '/../../include/bootstrap.php'; $cm = new \SwooleTest\CurlManager(); +$cm->disableNativeCurl(); $cm->run(function ($host) { // start testing diff --git a/tests/swoole_library/curl/basic/13.phpt b/tests/swoole_curl/basic/13.phpt similarity index 80% rename from tests/swoole_library/curl/basic/13.phpt rename to tests/swoole_curl/basic/13.phpt index 5d59aad8589..c1acdb1d393 100644 --- a/tests/swoole_library/curl/basic/13.phpt +++ b/tests/swoole_curl/basic/13.phpt @@ -1,9 +1,9 @@ --TEST-- -swoole_library/curl/basic: Test curl_opt() function with CURLOPT_HTTP_VERSION/CURL_HTTP_VERSION_1_1 +swoole_curl/basic: Test curl_opt() function with CURLOPT_HTTP_VERSION/CURL_HTTP_VERSION_1_1 --CREDITS-- TestFest 2009 - AFUP - Xavier Gorse --SKIPIF-- - + --FILE-- * Source code: ext/curl/interface.c * Alias to functions: */ -require __DIR__ . '/../../../include/bootstrap.php'; +require __DIR__ . '/../../include/bootstrap.php'; $cm = new \SwooleTest\CurlManager(); $cm->run(function ($host) { diff --git a/tests/swoole_curl/basic/14.phpt b/tests/swoole_curl/basic/14.phpt new file mode 100644 index 00000000000..8e950a2fbf8 --- /dev/null +++ b/tests/swoole_curl/basic/14.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_curl/basic: Test curl_init() function with basic functionality +--CREDITS-- +Jean-Marc Fontaine +--SKIPIF-- + + +--FILE-- +disableNativeCurl(); +$cm->run(function ($host) { + $ch = curl_init(); + Assert::isInstanceOf($ch, Swoole\Curl\Handler::class); + +}, false); + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/tests/swoole_curl/basic/15.phpt b/tests/swoole_curl/basic/15.phpt new file mode 100644 index 00000000000..173b54f6c37 --- /dev/null +++ b/tests/swoole_curl/basic/15.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_curl/basic: Test curl_init() function with $url parameter defined +--CREDITS-- +Jean-Marc Fontaine +--SKIPIF-- + + +--FILE-- +run(function ($host) { + $url = 'http://www.example.com/'; + $ch = curl_init($url); + + Assert::same($url, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); +}, false); + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/tests/swoole_curl/basic/19.phpt b/tests/swoole_curl/basic/19.phpt new file mode 100644 index 00000000000..7829ff2fd58 --- /dev/null +++ b/tests/swoole_curl/basic/19.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_curl/basic: Test curl_getinfo() function with CURLINFO_EFFECTIVE_URL parameter +--CREDITS-- +Jean-Marc Fontaine +--SKIPIF-- + +--FILE-- +run(function ($host) { + + $url = "http://{$host}/get.php?test="; + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_exec($ch); + $info = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); + var_dump($url == $info); + curl_close($ch); +}); +?> +===DONE=== +--EXPECTF-- +Hello World! +Hello World!bool(true) +===DONE=== diff --git a/tests/swoole_curl/basic/2.phpt b/tests/swoole_curl/basic/2.phpt new file mode 100644 index 00000000000..cc405dbcb92 --- /dev/null +++ b/tests/swoole_curl/basic/2.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_curl/basic: Test curl_opt() function with CURLOPT_RETURNTRANSFER parameter set to 1 +--CREDITS-- +Sebastian Deutsch +TestFest 2009 - AFUP - Jean-Marc Fontaine +--SKIPIF-- + +--FILE-- +run(function ($host) { + // start testing + echo '*** Testing curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); ***' . "\n"; + + $url = "{$host}/get.php?test=get"; + $ch = curl_init(); + + ob_start(); // start output buffering + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use + + $curl_content = curl_exec($ch); + curl_close($ch); + + var_dump( $curl_content ); +}); +?> +===DONE=== +--EXPECTF-- +*** Testing curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); *** +string(25) "Hello World! +Hello World!" +===DONE=== diff --git a/tests/swoole_curl/basic/20.phpt b/tests/swoole_curl/basic/20.phpt new file mode 100644 index 00000000000..b74a347c675 --- /dev/null +++ b/tests/swoole_curl/basic/20.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_curl/basic: Test curl_getinfo() function with CURLINFO_HTTP_CODE parameter +--CREDITS-- +Jean-Marc Fontaine +--SKIPIF-- + +--FILE-- +run(function ($host) { + $url = "{$host}/get.php?test="; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_exec($ch); + var_dump(curl_getinfo($ch, CURLINFO_HTTP_CODE)); + curl_close($ch); +}); +?> +===DONE=== +--EXPECTF-- +Hello World! +Hello World!int(200) +===DONE=== diff --git a/tests/swoole_curl/basic/21.phpt b/tests/swoole_curl/basic/21.phpt new file mode 100644 index 00000000000..4bc35b1b8d8 --- /dev/null +++ b/tests/swoole_curl/basic/21.phpt @@ -0,0 +1,27 @@ +--TEST-- +swoole_curl/basic: Test curl_getinfo() function with CURLINFO_CONTENT_TYPE parameter +--CREDITS-- +Jean-Marc Fontaine +--SKIPIF-- + +--FILE-- +run(function ($host) { + + $url = "{$host}/get.php?test=contenttype"; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_exec($ch); + var_dump(curl_getinfo($ch, CURLINFO_CONTENT_TYPE)); + curl_close($ch); +}); + +?> +===DONE=== +--EXPECTF-- +string(24) "text/plain;charset=utf-8" +===DONE=== diff --git a/tests/swoole_library/curl/basic/22.phpt b/tests/swoole_curl/basic/22.phpt similarity index 88% rename from tests/swoole_library/curl/basic/22.phpt rename to tests/swoole_curl/basic/22.phpt index 93f61146194..2aa6ab5c3bf 100644 --- a/tests/swoole_library/curl/basic/22.phpt +++ b/tests/swoole_curl/basic/22.phpt @@ -1,12 +1,12 @@ --TEST-- -swoole_library/curl/basic: Test curl_setopt() function with CURLOPT_FOLLOWLOCATION parameter +swoole_curl/basic: Test curl_setopt() function with CURLOPT_FOLLOWLOCATION parameter --CREDITS-- Jean-Marc Fontaine --SKIPIF-- - + --FILE-- run(function ($host) { diff --git a/tests/swoole_library/curl/basic/23.phpt b/tests/swoole_curl/basic/23.phpt similarity index 77% rename from tests/swoole_library/curl/basic/23.phpt rename to tests/swoole_curl/basic/23.phpt index e68d281d384..f8d1e1689c6 100644 --- a/tests/swoole_library/curl/basic/23.phpt +++ b/tests/swoole_curl/basic/23.phpt @@ -1,12 +1,12 @@ --TEST-- -swoole_library/curl/basic: Test curl_setopt() function with CURLOPT_HEADER parameter set to 1 +swoole_curl/basic: Test curl_setopt() function with CURLOPT_HEADER parameter set to 1 --CREDITS-- Jean-Marc Fontaine --SKIPIF-- - + --FILE-- run(function ($host) { diff --git a/tests/swoole_library/curl/basic/24.phpt b/tests/swoole_curl/basic/24.phpt similarity index 83% rename from tests/swoole_library/curl/basic/24.phpt rename to tests/swoole_curl/basic/24.phpt index 2575d89c567..b6d351d46ed 100644 --- a/tests/swoole_library/curl/basic/24.phpt +++ b/tests/swoole_curl/basic/24.phpt @@ -1,10 +1,10 @@ --TEST-- -swoole_library/curl/basic: Test curl_opt() function with setting auto referer +swoole_curl/basic: Test curl_opt() function with setting auto referer --CREDITS-- Sebastian Deutsch TestFest 2009 - AFUP - Jean-Marc Fontaine --SKIPIF-- - + --FILE-- * Alias to functions: */ -require __DIR__ . '/../../../include/bootstrap.php'; +require __DIR__ . '/../../include/bootstrap.php'; $cm = new \SwooleTest\CurlManager(); +$cm->disableNativeCurl(); $cm->run(function ($host) { // start testing diff --git a/tests/swoole_library/curl/basic/25.phpt b/tests/swoole_curl/basic/25.phpt similarity index 88% rename from tests/swoole_library/curl/basic/25.phpt rename to tests/swoole_curl/basic/25.phpt index d1cc087841d..baffab1e8e7 100644 --- a/tests/swoole_library/curl/basic/25.phpt +++ b/tests/swoole_curl/basic/25.phpt @@ -1,10 +1,10 @@ --TEST-- -swoole_library/curl/basic: Test curl_multi_getcontent() function +swoole_curl/basic: Test curl_multi_getcontent() function --CREDITS-- Sebastian Deutsch TestFest 2009 - AFUP - Jean-Marc Fontaine --SKIPIF-- - + --FILE-- * Source code: ext/curl/interface.c * Alias to functions: */ -require __DIR__ . '/../../../include/bootstrap.php'; +require __DIR__ . '/../../include/bootstrap.php'; $cm = new \SwooleTest\CurlManager(); $cm->run(function ($host) { // start testing diff --git a/tests/swoole_curl/basic/3.phpt b/tests/swoole_curl/basic/3.phpt new file mode 100644 index 00000000000..134dbd876e7 --- /dev/null +++ b/tests/swoole_curl/basic/3.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_curl/basic: Test curl_opt() function with POST parameters +--CREDITS-- +Sebastian Deutsch +TestFest 2009 - AFUP - Jean-Marc Fontaine +--SKIPIF-- + +--FILE-- +run(function ($host) { + + // start testing + echo '*** Testing curl sending through GET an POST ***' . "\n"; + + $url = "{$host}/get.php?test=getpost&get_param=Hello%20World"; + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, "Hello=World&Foo=Bar&Person=John%20Doe"); + curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use + + $curl_content = curl_exec($ch); + curl_close($ch); + + var_dump( $curl_content ); +}); +?> +===DONE=== +--EXPECTF-- +*** Testing curl sending through GET an POST *** +string(208) "array(2) { + ["test"]=> + string(7) "getpost" + ["get_param"]=> + string(11) "Hello World" +} +array(3) { + ["Hello"]=> + string(5) "World" + ["Foo"]=> + string(3) "Bar" + ["Person"]=> + string(8) "John Doe" +} +" +===DONE=== diff --git a/tests/swoole_curl/basic/4.phpt b/tests/swoole_curl/basic/4.phpt new file mode 100644 index 00000000000..9595dcc176d --- /dev/null +++ b/tests/swoole_curl/basic/4.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_curl/basic: Test curl_opt() function with setting referer +--CREDITS-- +Sebastian Deutsch +TestFest 2009 - AFUP - Jean-Marc Fontaine +--SKIPIF-- + +--FILE-- +run(function ($host) { + + // start testing + echo '*** Testing curl setting referer ***' . "\n"; + + $url = "{$host}/get.php?test=referer"; + $ch = curl_init(); + + ob_start(); // start output buffering + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_REFERER, 'http://www.refer.er'); + curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use + + $curl_content = curl_exec($ch); + curl_close($ch); + + var_dump($curl_content); +}); + +?> +===DONE=== +--EXPECTF-- +*** Testing curl setting referer *** +string(19) "http://www.refer.er" +===DONE=== diff --git a/tests/swoole_curl/basic/5.phpt b/tests/swoole_curl/basic/5.phpt new file mode 100644 index 00000000000..2536771ccf3 --- /dev/null +++ b/tests/swoole_curl/basic/5.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_curl/basic: Test curl_opt() function with user agent +--CREDITS-- +Sebastian Deutsch +TestFest 2009 - AFUP - Jean-Marc Fontaine +--SKIPIF-- + +--FILE-- +run(function ($host) { + // start testing + echo '*** Testing curl with user agent ***' . "\n"; + + $url = "{$host}/get.php?test=useragent"; + $ch = curl_init(); + + ob_start(); // start output buffering + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_USERAGENT, 'cURL phpt'); + curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use + + $curl_content = curl_exec($ch); + curl_close($ch); + + var_dump( $curl_content ); + +}); + + +?> +===DONE=== +--EXPECTF-- +*** Testing curl with user agent *** +string(9) "cURL phpt" +===DONE=== diff --git a/tests/swoole_curl/basic/7.phpt b/tests/swoole_curl/basic/7.phpt new file mode 100644 index 00000000000..53f14f45136 --- /dev/null +++ b/tests/swoole_curl/basic/7.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_curl/basic: Test curl_error() & curl_errno() function without url +--CREDITS-- +TestFest 2009 - AFUP - Perrick Penet +--SKIPIF-- + + +--FILE-- +run(function ($host) { + + $ch = curl_init(); + + curl_exec($ch); + var_dump(curl_error($ch)); + var_dump(curl_errno($ch)); + curl_close($ch); + +}, false); + +?> +--EXPECTF-- +%string(%d) "No URL set%s" +int(3) diff --git a/tests/swoole_curl/basic/8.phpt b/tests/swoole_curl/basic/8.phpt new file mode 100644 index 00000000000..243efbdc606 --- /dev/null +++ b/tests/swoole_curl/basic/8.phpt @@ -0,0 +1,30 @@ +--TEST-- +swoole_curl/basic: Test curl_error() & curl_errno() function with problematic host +--CREDITS-- +TestFest 2009 - AFUP - Perrick Penet +--SKIPIF-- + +--FILE-- +run(function ($host) { + $url = "http://www." . uniqid() . "." . uniqid(); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_exec($ch); + var_dump(curl_error($ch)); + var_dump(curl_errno($ch)); + curl_close($ch); +}, false); +?> +--EXPECTF-- +%s resolve%s +int(6) diff --git a/tests/swoole_curl/basic/9.phpt b/tests/swoole_curl/basic/9.phpt new file mode 100644 index 00000000000..c52988e9221 --- /dev/null +++ b/tests/swoole_curl/basic/9.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_curl/basic: Test curl_error() & curl_errno() function with problematic protocol +--CREDITS-- +TestFest 2009 - AFUP - Perrick Penet +--SKIPIF-- + + +--FILE-- +disableNativeCurl(); +$cm->run(function ($host) { + $url = uniqid()."://www.".uniqid().".".uniqid(); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + + curl_exec($ch); + var_dump(curl_error($ch)); + var_dump(curl_errno($ch)); + curl_close($ch); + +}, false); +?> +--EXPECTREGEX-- +string\(\d+\) ".+URL.+" +int\(\d\) diff --git a/tests/swoole_curl/cancel.phpt b/tests/swoole_curl/cancel.phpt new file mode 100644 index 00000000000..1a6370eb742 --- /dev/null +++ b/tests/swoole_curl/cancel.phpt @@ -0,0 +1,68 @@ +--TEST-- +swoole_curl: sleep +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + $s = microtime(true); + run(function () use ($pm) { + $ch = curl_init(); + $code = uniqid('swoole_'); + $url = "http://127.0.0.1:".$pm->getFreePort()."/?code=".urlencode($code); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + + $cid = Coroutine::getCid(); + go(function () use ($cid) { + System::sleep(0.01); + Assert::true(Coroutine::cancel($cid)); + Assert::false(Coroutine::isCanceled()); + }); + + $output = curl_exec($ch); + Assert::isEmpty($output); + Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED); + Assert::eq(curl_errno($ch), CURLE_ABORTED_BY_CALLBACK); + Assert::contains(curl_error($ch), 'Operation was aborted by an application callback'); + curl_close($ch); + }); + Assert::lessThan(microtime(true) - $s, 0.5); + $pm->kill(); + echo "Done\n"; +}; +$pm->childFunc = function () use ($pm) { + $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(); + }); + + $http->on("request", function (Request $request, Response $response) { + usleep(300000); + $response->end("Hello World\n".$request->get['code']); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +Done diff --git a/tests/swoole_runtime/curl_native/close_before_resume.phpt b/tests/swoole_curl/close_before_resume.phpt similarity index 85% rename from tests/swoole_runtime/curl_native/close_before_resume.phpt rename to tests/swoole_curl/close_before_resume.phpt index 0f8ae4c193b..73e670c2485 100644 --- a/tests/swoole_runtime/curl_native/close_before_resume.phpt +++ b/tests/swoole_curl/close_before_resume.phpt @@ -1,12 +1,12 @@ --TEST-- -swoole_runtime/curl_native: close before resume +swoole_curl: close before resume --SKIPIF-- --FILE-- parentFunc = function () use ($pm) { if ($output === false) { echo "CURL Error:" . curl_error($ch); } - Assert::true(curl_close($ch)); + curl_close($ch); echo "close [2]\n"; }); Assert::lessThan(microtime(true) - $s, 0.5); @@ -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) { @@ -71,9 +71,8 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -Fatal error: Uncaught Swoole\Error: The cURL client is executing, this handle cannot be operated in %s:%d +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 new file mode 100644 index 00000000000..74de7cda808 --- /dev/null +++ b/tests/swoole_curl/concurrent.phpt @@ -0,0 +1,77 @@ +--TEST-- +swoole_curl: Concurrent request +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + $s = microtime(true); + run(function () use ($pm) { + $fn = function () use ($pm) { + $ch = curl_init(); + $code = uniqid('swoole_'); + $url = "http://127.0.0.1:".$pm->getFreePort()."/?code=".urlencode($code); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) { + return strlen($strHeader); + }); + $output = curl_exec($ch); + Assert::eq($output, "Hello World\n".$code); + if ($output === false) { + echo "CURL Error:" . curl_error($ch); + } + curl_close($ch); + echo "DONE\n"; + }; + + $n = N; + while($n--) { + go($fn); + } + }); + Assert::lessThan(microtime(true) - $s, 0.5); + $pm->kill(); +}; +$pm->childFunc = function () use ($pm) { + $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) { + $pm->wakeup(); + }); + + $http->on("request", function (Request $request, Response $response) { + usleep(rand(1000, 30000)); + $response->end("Hello World\n".$request->get['code']); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE +DONE +DONE +DONE +DONE +DONE +DONE +DONE diff --git a/tests/swoole_curl/error.phpt b/tests/swoole_curl/error.phpt new file mode 100644 index 00000000000..d543910b3c6 --- /dev/null +++ b/tests/swoole_curl/error.phpt @@ -0,0 +1,41 @@ +--TEST-- +swoole_curl: error +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done 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_runtime/curl_native/exec_twice.phpt b/tests/swoole_curl/exec_twice.phpt similarity index 81% rename from tests/swoole_runtime/curl_native/exec_twice.phpt rename to tests/swoole_curl/exec_twice.phpt index a9b3b7093e4..012b1957506 100644 --- a/tests/swoole_runtime/curl_native/exec_twice.phpt +++ b/tests/swoole_curl/exec_twice.phpt @@ -1,12 +1,12 @@ --TEST-- -swoole_runtime/curl_native: close before resume +swoole_curl: exec twice --SKIPIF-- --FILE-- parentFunc = function () use ($pm) { go(function() use ($ch) { Co::sleep(0.1); - var_dump(curl_exec($ch)); + echo "co 2 exec\n"; + var_dump(curl_exec($ch), curl_errno($ch)); }); + echo "co 1 exec\n"; $output = curl_exec($ch); Assert::eq($output, "Hello World\n".$code); if ($output === false) { echo "CURL Error:" . curl_error($ch); } - Assert::true(curl_close($ch)); + curl_close($ch); echo "close [2]\n"; }); Assert::lessThan(microtime(true) - $s, 0.5); @@ -50,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) { @@ -67,8 +69,11 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -Fatal error: Uncaught Swoole\Error: The cURL client is executing, this handle cannot be operated in %s:%d +co 1 exec +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 new file mode 100644 index 00000000000..cc25f8c30d5 --- /dev/null +++ b/tests/swoole_curl/guzzle.phpt @@ -0,0 +1,71 @@ +--TEST-- +swoole_curl: guzzle +--SKIPIF-- + +--FILE-- + $client->getAsync('http://www.baidu.com/'), + 'qq' => $client->getAsync('https://www.qq.com/'), + 'zhihu' => $client->getAsync('http://www.zhihu.com/') + ]; + $responses = Promise\Utils::unwrap($promises); + Assert::contains($responses['baidu']->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' => 'https://httpbin.org/']); + $n = N; + $data = $promises = []; + while ($n--) { + $key = 'req_' . $n; + $data[$key] = uniqid('swoole_test'); + $promises[$key] = $client->getAsync('/base64/' . base64_encode($data[$key])); + } + $responses = Promise\Utils::unwrap($promises); + + $n = N; + while ($n--) { + $key = 'req_' . $n; + Assert::eq($responses[$key]->getBody(), $data[$key]); + } + $result['task_2'] = 'OK'; + }); + + Barrier::wait($barrier); + Assert::eq($result['task_1'], 'OK'); + Assert::eq($result['task_2'], 'OK'); + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/guzzle/cancel.phpt b/tests/swoole_curl/guzzle/cancel.phpt new file mode 100644 index 00000000000..9419dc9a0be --- /dev/null +++ b/tests/swoole_curl/guzzle/cancel.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_curl/guzzle: cancel +--SKIPIF-- + +--FILE-- +cancel(); + $responses[] = $response; + } + foreach ($responses as $r) { + Assert::true(Promise\Is::rejected($r)); + } + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/guzzle/cannot_cancel_finished.phpt b/tests/swoole_curl/guzzle/cannot_cancel_finished.phpt new file mode 100644 index 00000000000..75818f55465 --- /dev/null +++ b/tests/swoole_curl/guzzle/cannot_cancel_finished.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_curl/guzzle: cannot_cancel_finished +--SKIPIF-- + +--FILE-- +wait(); + $response->cancel(); + Assert::true(Promise\Is::fulfilled($response)); + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/guzzle/cookie.phpt b/tests/swoole_curl/guzzle/cookie.phpt new file mode 100644 index 00000000000..50d57d11973 --- /dev/null +++ b/tests/swoole_curl/guzzle/cookie.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_curl/guzzle: cookie +--SKIPIF-- + +--FILE-- + 'foo', + 'other_cookie' => 'barbaz1234' + ], + 'httpbin.org' + ); + $r = $client->request('GET', 'http://httpbin.org/cookies', [ + 'cookies' => $jar + ]); + Assert::eq($r->getStatusCode(), 200); + Assert::eq(json_decode($r->getBody()->getContents(), true)['cookies']['some_cookie'], 'foo'); + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/guzzle/promise.phpt b/tests/swoole_curl/guzzle/promise.phpt new file mode 100644 index 00000000000..37a5bcfc5c2 --- /dev/null +++ b/tests/swoole_curl/guzzle/promise.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_curl/guzzle: promise +--SKIPIF-- + +--FILE-- + '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!']]), + 'c' => $client->requestAsync('POST', '/post', ['json' => ['data' => 'hello test3!']]), + ]; + + // Wait on all of the requests to complete. + $results = GuzzleHttp\Promise\Utils::unwrap($promises); + + // You can access each result using the key provided to the unwrap + // function. + echo json_decode($results['a']->getBody()->getContents())->data . PHP_EOL; + echo $results['b']->getHeaderLine('Content-Type') . PHP_EOL; + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +{"data":"hello test1!"} +application/json +Done diff --git a/tests/swoole_curl/guzzle/request_async.phpt b/tests/swoole_curl/guzzle/request_async.phpt new file mode 100644 index 00000000000..8f6f4cfed4e --- /dev/null +++ b/tests/swoole_curl/guzzle/request_async.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_curl/guzzle: request async +--SKIPIF-- + +--FILE-- +requestAsync('GET', 'http://httpbin.org/get'); + $promise->then( + function (ResponseInterface $res) { + echo $res->getStatusCode() .PHP_EOL; + }, + function (RequestException $e) { + echo $e->getMessage() . PHP_EOL; + echo $e->getRequest()->getMethod() . PHP_EOL; + } + ); + $promise->wait(); + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +200 +Done diff --git a/tests/swoole_curl/guzzle/request_on_stats.phpt b/tests/swoole_curl/guzzle/request_on_stats.phpt new file mode 100644 index 00000000000..7d191231c6e --- /dev/null +++ b/tests/swoole_curl/guzzle/request_on_stats.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_curl/guzzle: request on_stats +--SKIPIF-- + +--FILE-- +request('GET', $host, [ + 'on_stats' => function (TransferStats $stats) use ($host) { + Assert::eq($stats->getEffectiveUri(), $host); + + // You must check if a response was received before using the + // response object. + if ($stats->hasResponse()) { + Assert::eq($stats->getResponse()->getStatusCode(), 200); + } else { + // Error data is handler specific. You will need to know what + // type of error data your handler uses before using this + // value. + var_dump($stats->getHandlerErrorData()); + } + } + ]); + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/guzzle/send_async.phpt b/tests/swoole_curl/guzzle/send_async.phpt new file mode 100644 index 00000000000..33bf8b4b6b7 --- /dev/null +++ b/tests/swoole_curl/guzzle/send_async.phpt @@ -0,0 +1,41 @@ +--TEST-- +swoole_curl/guzzle: send async +--SKIPIF-- + +--FILE-- +request('GET', 'https://httpbin.org'); + + echo $response->getStatusCode(), PHP_EOL; // 200 + echo $response->getHeaderLine('content-type'), PHP_EOL; + + // Send an asynchronous request. + $request = new Request('GET', 'http://httpbin.org'); + $promise = $client->sendAsync($request)->then(function ($response) { + echo 'I completed! ' . $response->getStatusCode() . PHP_EOL; + }); + + $promise->wait(); + echo 'Done' . PHP_EOL; +}); +?> +--EXPECT-- +200 +text/html; charset=utf-8 +I completed! 200 +Done diff --git a/tests/swoole_runtime/curl_native/https.phpt b/tests/swoole_curl/https.phpt similarity index 84% rename from tests/swoole_runtime/curl_native/https.phpt rename to tests/swoole_curl/https.phpt index 221dd802c2b..6b6cb605122 100644 --- a/tests/swoole_runtime/curl_native/https.phpt +++ b/tests/swoole_curl/https.phpt @@ -1,12 +1,12 @@ --TEST-- -swoole_runtime/curl_native: https +swoole_curl: https --SKIPIF-- --FILE-- +--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/bug48203_multi.phpt b/tests/swoole_curl/multi/bug48203_multi.phpt new file mode 100644 index 00000000000..b5ba15104a0 --- /dev/null +++ b/tests/swoole_curl/multi/bug48203_multi.phpt @@ -0,0 +1,95 @@ +--TEST-- +swoole_curl/multi: Variation of bug #48203 with curl_multi_exec (Crash when file pointers passed to curl are closed before calling curl_multi_exec) +--SKIPIF-- + +--FILE-- + 1, + $curl_option => $fp, + CURLOPT_URL => $target_url, + ); + + // we also need to set CURLOPT_VERBOSE to test CURLOPT_STDERR properly + if (CURLOPT_STDERR == $curl_option) { + $options[CURLOPT_VERBOSE] = 1; + } + + if (CURLOPT_INFILE == $curl_option) { + $options[CURLOPT_UPLOAD] = 1; + } + + curl_setopt_array($ch1, $options); + curl_setopt_array($ch2, $options); + + fclose($fp); // <-- premature close of $fp caused a crash! + + $mh = curl_multi_init(); + + curl_multi_add_handle($mh, $ch1); + curl_multi_add_handle($mh, $ch2); + + $active = 0; + do { + curl_multi_exec($mh, $active); + } while ($active > 0); + + curl_multi_remove_handle($mh, $ch1); + curl_multi_remove_handle($mh, $ch2); + curl_multi_close($mh); + + // Force curl to output results + fflush(STDERR); + fflush(STDOUT); + + echo "Ok for $description\n"; +} + +$cm = new CurlManager(); +$cm->run(function ($host) { + $url = "{$host}/"; + + $options_to_check = array( + "CURLOPT_STDERR", "CURLOPT_WRITEHEADER", "CURLOPT_FILE", "CURLOPT_INFILE" + ); + + foreach($options_to_check as $option) { + checkForClosedFilePointer($url, constant($option), $option); + } +}); +?> +--CLEAN-- + +--EXPECTF-- +Warning: curl_multi_add_handle(): CURLOPT_STDERR resource has gone away, resetting to stderr in %s on line %d +%A +Warning: curl_multi_add_handle(): CURLOPT_STDERR resource has gone away, resetting to stderr in %s on line %d +%A +Ok for CURLOPT_STDERR + +Warning: curl_multi_add_handle(): CURLOPT_WRITEHEADER resource has gone away, resetting to default in %s on line %d + +Warning: curl_multi_add_handle(): CURLOPT_WRITEHEADER resource has gone away, resetting to default in %s on line %d +Ok for CURLOPT_WRITEHEADER + +Warning: curl_multi_add_handle(): CURLOPT_FILE resource has gone away, resetting to default in %s on line %d + +Warning: curl_multi_add_handle(): CURLOPT_FILE resource has gone away, resetting to default in %s on line %d +%AOk for CURLOPT_FILE + +Warning: curl_multi_add_handle(): CURLOPT_INFILE resource has gone away, resetting to default in %s on line %d + +Warning: curl_multi_add_handle(): CURLOPT_INFILE resource has gone away, resetting to default in %s on line %d +Ok for CURLOPT_INFILE diff --git a/tests/swoole_curl/multi/bug67643.phpt b/tests/swoole_curl/multi/bug67643.phpt new file mode 100644 index 00000000000..462561f4224 --- /dev/null +++ b/tests/swoole_curl/multi/bug67643.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_curl/multi: Bug #67643 (curl_multi_getcontent returns '' when RETURNTRANSFER isn't set) +--SKIPIF-- + + +--FILE-- + 0); + + $results = curl_multi_getcontent($ch); + + curl_multi_remove_handle($mh, $ch); + curl_multi_close($mh); + + Assert::contains($results, 'Bug #67643'); + echo 'Done'.PHP_EOL; +}); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/multi/bug71523.phpt b/tests/swoole_curl/multi/bug71523.phpt new file mode 100644 index 00000000000..373b9f4d54a --- /dev/null +++ b/tests/swoole_curl/multi/bug71523.phpt @@ -0,0 +1,36 @@ +--TEST-- +swoole_curl/multi: Bug #71523 (Copied handle with new option CURLOPT_HTTPHEADER crashes while curl_multi_exec) +--SKIPIF-- + + +--FILE-- + +okey +--EXPECT-- +okey diff --git a/tests/swoole_curl/multi/bug76675.phpt b/tests/swoole_curl/multi/bug76675.phpt new file mode 100644 index 00000000000..2f9b8c4d9d0 --- /dev/null +++ b/tests/swoole_curl/multi/bug76675.phpt @@ -0,0 +1,70 @@ +--TEST-- +swoole_curl/multi: Bug #76675 (Segfault with H2 server push write/writeheader handlers) +--SKIPIF-- + + +--FILE-- + +--EXPECTREGEX-- +(Received \d+)+ diff --git a/tests/swoole_curl/multi/bug77535.phpt b/tests/swoole_curl/multi/bug77535.phpt new file mode 100644 index 00000000000..5e91161faa2 --- /dev/null +++ b/tests/swoole_curl/multi/bug77535.phpt @@ -0,0 +1,94 @@ +--TEST-- +swoole_curl/multi: Bug #77535 (Invalid callback, h2 server push) +--SKIPIF-- + + +--FILE-- +mh = curl_multi_init()) { + throw new \RuntimeException('Unable to create a new cURL multi handle'); + } + + $this->addServerPushCallback(); + echo __METHOD__.'[2]'.PHP_EOL; + + $this->curl = curl_init(); + curl_setopt($this->curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + curl_setopt($this->curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); + 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, 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); + }); + curl_setopt($this->curl, CURLOPT_WRITEFUNCTION, function ($ch, $data) { + return \strlen($data); + }); + curl_multi_add_handle($this->mh, $this->curl); + echo __METHOD__.'[3]'.PHP_EOL; + + $stillRunning = null; + while (true) { + do { + $mrc = curl_multi_exec($this->mh, $stillRunning); + } while (CURLM_CALL_MULTI_PERFORM === $mrc); + + $info = curl_multi_info_read($this->mh); + while (false !== $info && $info['msg'] == CURLMSG_DONE) { + if (CURLMSG_DONE !== $info['msg']) { + continue; + } + echo "Start handle request.\n"; + return; + } + } + } + + private function addServerPushCallback(): void + { + echo __METHOD__.PHP_EOL; + $callback = static function () { + return CURL_PUSH_OK; + }; + + curl_multi_setopt($this->mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX); + curl_multi_setopt($this->mh, CURLMOPT_PUSHFUNCTION, $callback); + } +} + +use Swoole\Runtime; + +use function Swoole\Coroutine\run; +Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + +run(function () { + $buzz = new MyHttpClient(); + $buzz->sendRequest(); + $buzz = null; +}); +?> +--EXPECT-- +MyHttpClient::sendRequest[1] +MyHttpClient::addServerPushCallback +MyHttpClient::sendRequest[2] +MyHttpClient::sendRequest[3] +Start handle request. diff --git a/tests/swoole_curl/multi/bug77946.phpt b/tests/swoole_curl/multi/bug77946.phpt new file mode 100644 index 00000000000..29ab6be4ddb --- /dev/null +++ b/tests/swoole_curl/multi/bug77946.phpt @@ -0,0 +1,52 @@ +--TEST-- +swoole_curl/multi: Bug #77946 (Errored cURL resources returned by curl_multi_info_read() must be compatible with curl_errno() and curl_error()) +--SKIPIF-- + + +--FILE-- + $url) { + $conn[$i] = curl_init($url); + curl_multi_add_handle($mh, $conn[$i]); + } + + do { + $status = curl_multi_exec($mh, $active); + $info = curl_multi_info_read($mh); + if (false !== $info) { + var_dump($info['result']); + var_dump(curl_errno($info['handle'])); + var_dump(curl_error($info['handle'])); + } + } while ($status === CURLM_CALL_MULTI_PERFORM || $active); + + foreach ($urls as $i => $url) { + curl_close($conn[$i]); + } + + curl_multi_close($mh); +}); +?> +--EXPECTF-- +int(1) +int(1) +string(%d) "Protocol %Sunknown%S not supported or disabled in libcurl" diff --git a/tests/swoole_curl/multi/curl_basic_018.phpt b/tests/swoole_curl/multi/curl_basic_018.phpt new file mode 100644 index 00000000000..6609d48603a --- /dev/null +++ b/tests/swoole_curl/multi/curl_basic_018.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_curl/multi: Test curl_setopt() with curl_multi function with basic functionality +--CREDITS-- +TestFest 2009 - AFUP - Thomas Rabaix +--SKIPIF-- + +--FILE-- +run(function ($host) { + // start testing + echo "*** Testing curl_exec() : basic functionality ***\n"; + + $url = "{$host}/get.inc?test=get"; + $chs = array( + 0 => curl_init(), + 1 => curl_init(), + 2 => curl_init(), + ); + + ob_start(); // start output buffering + + $options = array( + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_URL => $url, + ); + + curl_setopt_array($chs[0], $options); //set the options + curl_setopt_array($chs[1], $options); //set the options + curl_setopt_array($chs[2], $options); //set the options + + $mh = curl_multi_init(); + + // add handlers + curl_multi_add_handle($mh, $chs[0]); + curl_multi_add_handle($mh, $chs[1]); + curl_multi_add_handle($mh, $chs[2]); + + $running=null; + //execute the handles + do { + curl_multi_exec($mh, $running); + } while ($running > 0); + + $curl_content = ''; + $curl_content .= curl_multi_getcontent($chs[0]); + $curl_content .= curl_multi_getcontent($chs[1]); + $curl_content .= curl_multi_getcontent($chs[2]); + + //close the handles + curl_multi_remove_handle($mh, $chs[0]); + curl_multi_remove_handle($mh, $chs[1]); + curl_multi_remove_handle($mh, $chs[2]); + curl_multi_close($mh); + + var_dump( $curl_content ); +}); +?> +--EXPECT-- +*** Testing curl_exec() : basic functionality *** +string(75) "Hello World! +Hello World!Hello World! +Hello World!Hello World! +Hello World!" diff --git a/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt b/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt new file mode 100644 index 00000000000..114e2d3c4e0 --- /dev/null +++ b/tests/swoole_curl/multi/curl_copy_handle_variation4.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_curl/multi: curl_copy_handle() allows to post CURLFile multiple times with curl_multi_exec() +--SKIPIF-- + +--FILE-- +run(function ($host) { + $ch1 = curl_init(); + curl_setopt($ch1, CURLOPT_SAFE_UPLOAD, 1); + curl_setopt($ch1, CURLOPT_URL, "{$host}/get.php?test=file"); + // curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); + + $filename = __DIR__ . '/АБВ.txt'; + file_put_contents($filename, "Test."); + $file = curl_file_create($filename); + $params = array('file' => $file); + var_dump(curl_setopt($ch1, CURLOPT_POSTFIELDS, $params)); + + $ch2 = curl_copy_handle($ch1); + $ch3 = curl_copy_handle($ch1); + + $mh = curl_multi_init(); + curl_multi_add_handle($mh, $ch1); + curl_multi_add_handle($mh, $ch2); + do { + $status = curl_multi_exec($mh, $active); + if ($active) { + curl_multi_select($mh); + } + } while ($active && $status == CURLM_OK); + + curl_multi_remove_handle($mh, $ch1); + curl_multi_remove_handle($mh, $ch2); + 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=== +--CLEAN-- + diff --git a/tests/swoole_curl/multi/curl_int_cast.phpt b/tests/swoole_curl/multi/curl_int_cast.phpt new file mode 100644 index 00000000000..c045f0908a9 --- /dev/null +++ b/tests/swoole_curl/multi/curl_int_cast.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_curl/multi: Casting CurlHandle to int returns object ID +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +int(%d) +int(%d) +int(%d) +int(%d) diff --git a/tests/swoole_curl/multi/curl_multi_close_basic.phpt b/tests/swoole_curl/multi/curl_multi_close_basic.phpt new file mode 100644 index 00000000000..bb433dbae3c --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_close_basic.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_curl/multi: curl_multi_close +--CREDITS-- +Stefan Koopmanschap +#testfest Utrecht 2009 +--SKIPIF-- + + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_curl/multi/curl_multi_close_basic001.phpt b/tests/swoole_curl/multi/curl_multi_close_basic001.phpt new file mode 100644 index 00000000000..95ce6225507 --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_close_basic001.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_curl/multi: curl_multi_close return false when supplied resource not valid cURL multi handle +--SKIPIF-- + + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_curl/multi/curl_multi_close_reference.phpt b/tests/swoole_curl/multi/curl_multi_close_reference.phpt new file mode 100644 index 00000000000..b948df0d957 --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_close_reference.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_curl/multi: curl_multi_close closed by cleanup functions +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +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 new file mode 100644 index 00000000000..fbdfc8a034b --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_errno_strerror_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_curl/multi: curl_multi_errno and curl_multi_strerror basic test +--SKIPIF-- + + +--FILE-- +getMessage() . "\n"; + } + + $errno = curl_multi_errno($mh); + echo $errno . PHP_EOL; + echo curl_multi_strerror($errno) . PHP_EOL; + }); +}); +$output = $pm->getChildOutput(); + +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_getcontent_basic3.phpt b/tests/swoole_curl/multi/curl_multi_getcontent_basic3.phpt new file mode 100644 index 00000000000..f2a2fabf245 --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_getcontent_basic3.phpt @@ -0,0 +1,63 @@ +--TEST-- +swoole_curl/multi: Curl_multi_getcontent() basic test with different sources (local file/http) +--CREDITS-- +Rein Velt (rein@velt.org) +#TestFest Utrecht 20090509 +--SKIPIF-- + +--FILE-- +run(function ($host) { + //CURL_MULTI_GETCONTENT TEST + + //CREATE RESOURCES + $ch1 = curl_init(); + $ch2 = curl_init(); + + //SET URL AND OTHER OPTIONS + curl_setopt($ch1, CURLOPT_URL, "{$host}/get.inc?test=getpost&get_param=Hello%20World"); + curl_setopt($ch2, CURLOPT_URL, "file://" . __DIR__ . DIRECTORY_SEPARATOR . "curl_testdata2.txt"); + curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); + + //CREATE MULTIPLE CURL HANDLE + $mh = curl_multi_init(); + + //ADD THE 2 HANDLES + curl_multi_add_handle($mh, $ch1); + curl_multi_add_handle($mh, $ch2); + + //EXECUTE + $running = 0; + do { + curl_multi_exec($mh, $running); + } while ($running > 0); + + $results1 = curl_multi_getcontent($ch1); + $results2 = curl_multi_getcontent($ch2); + + //CLOSE + curl_multi_remove_handle($mh, $ch1); + curl_multi_remove_handle($mh, $ch2); + curl_multi_close($mh); + + echo $results1; + echo $results2; +}); +echo "CURL2\n"; +?> +--EXPECT-- +array(2) { + ["test"]=> + string(7) "getpost" + ["get_param"]=> + string(11) "Hello World" +} +array(0) { +} +CURL2 diff --git a/tests/swoole_curl/multi/curl_multi_info_read.phpt b/tests/swoole_curl/multi/curl_multi_info_read.phpt new file mode 100644 index 00000000000..97f9241a942 --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_info_read.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_curl/multi: array curl_multi_info_read ( resource $mh [, int &$msgs_in_queue = NULL ] ); +--CREDITS-- +marcosptf - - @phpsp - sao paulo - br +--SKIPIF-- + + +--FILE-- + $url) { + $conn[$i] = curl_init($url); + curl_setopt($conn[$i], CURLOPT_RETURNTRANSFER, 1); + curl_multi_add_handle($mh, $conn[$i]); + } + + do { + $status = curl_multi_exec($mh, $active); + } while ($status === CURLM_CALL_MULTI_PERFORM || $active); + + while ($info = curl_multi_info_read($mh)) { + Assert::count($info, 3); + } + + foreach ($urls as $i => $url) { + curl_close($conn[$i]); + } +}); +?> +--EXPECT-- diff --git a/tests/swoole_curl/multi/curl_multi_init_basic.phpt b/tests/swoole_curl/multi/curl_multi_init_basic.phpt new file mode 100644 index 00000000000..c043cb1f7b4 --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_init_basic.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_curl/multi: Test curl_multi_init() +--CREDITS-- +Mark van der Velden +#testfest Utrecht 2009 +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +*** Testing curl_multi_init(void); *** diff --git a/tests/swoole_curl/multi/curl_multi_segfault.phpt b/tests/swoole_curl/multi/curl_multi_segfault.phpt new file mode 100644 index 00000000000..7ca52d0a68c --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_segfault.phpt @@ -0,0 +1,62 @@ +--TEST-- +swoole_curl/multi: Segfault due to libcurl connection caching +--SKIPIF-- + + +--FILE-- + +--EXPECT-- +bool(true) diff --git a/tests/swoole_curl/multi/curl_multi_select_basic1.phpt b/tests/swoole_curl/multi/curl_multi_select_basic1.phpt new file mode 100644 index 00000000000..306d3ce3c15 --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_select_basic1.phpt @@ -0,0 +1,26 @@ +--TEST-- +swoole_curl/multi: Test curl_multi_select() +--CREDITS-- +Ivo Jansch +#testfest Utrecht 2009 +--SKIPIF-- + + +--FILE-- + +--EXPECTF-- +%r(0|-1)%r diff --git a/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt b/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt new file mode 100644 index 00000000000..202d7133846 --- /dev/null +++ b/tests/swoole_curl/multi/curl_multi_setopt_basic001.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_curl/multi: curl_multi_setopt basic test +--SKIPIF-- + + +--FILE-- +getMessage() . "\n"; + } + curl_multi_close($mh); + }); +}); +$output = $pm->getChildOutput(); + +Assert::contains($output, 'bool(true)'); + +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/multi_1.phpt b/tests/swoole_curl/multi_1.phpt new file mode 100644 index 00000000000..c025f1c59ce --- /dev/null +++ b/tests/swoole_curl/multi_1.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_curl: multi 1 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_curl/multi_2.phpt b/tests/swoole_curl/multi_2.phpt new file mode 100644 index 00000000000..76814b30727 --- /dev/null +++ b/tests/swoole_curl/multi_2.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_curl: multi 2 +--SKIPIF-- + +--FILE-- + 0.2]); + echo "Done\n"; +}); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/multi_3.phpt b/tests/swoole_curl/multi_3.phpt new file mode 100644 index 00000000000..dd723c57461 --- /dev/null +++ b/tests/swoole_curl/multi_3.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_curl: multi 3 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done +Done +Done +Done diff --git a/tests/swoole_curl/multi_4.phpt b/tests/swoole_curl/multi_4.phpt new file mode 100644 index 00000000000..145191f090a --- /dev/null +++ b/tests/swoole_curl/multi_4.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_curl: multi 4 +--SKIPIF-- + +--FILE-- + 0.2]); + echo "Done\n"; + }); + } +}); +?> +--EXPECT-- +Done +Done +Done +Done diff --git a/tests/swoole_curl/multi_close.phpt b/tests/swoole_curl/multi_close.phpt new file mode 100644 index 00000000000..ff7546c7e6b --- /dev/null +++ b/tests/swoole_curl/multi_close.phpt @@ -0,0 +1,31 @@ +--TEST-- +swoole_curl: clean handle +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_curl/multi_dtor.phpt b/tests/swoole_curl/multi_dtor.phpt new file mode 100644 index 00000000000..cdd708f7660 --- /dev/null +++ b/tests/swoole_curl/multi_dtor.phpt @@ -0,0 +1,29 @@ +--TEST-- +swoole_curl: multi dtor +--SKIPIF-- + +--FILE-- + +--EXPECT-- +Done diff --git a/tests/swoole_curl/non_exclusive.phpt b/tests/swoole_curl/non_exclusive.phpt new file mode 100644 index 00000000000..ee886174b25 --- /dev/null +++ b/tests/swoole_curl/non_exclusive.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_curl: non-exclusive +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + run(function () use ($pm) { + $ch = curl_init(); + $code = uniqid('swoole_'); + $url = "http://127.0.0.1:".$pm->getFreePort()."/?code=".urlencode($code); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + + $header_count = 0; + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) use (&$header_count) { + Assert::eq(curl_getinfo($ch, CURLINFO_HTTP_CODE), 200); + $header_count++; + return strlen($strHeader); + }); + + $output = curl_exec($ch); + Assert::eq($output, "Hello World\n".$code); + if ($output === false) { + echo "CURL Error:" . curl_error($ch); + } + Assert::greaterThan($header_count, 1); + curl_close($ch); + echo "Close\n"; + }); + $pm->kill(); + echo "Done\n"; +}; +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); + $http->set(['worker_num' => 1, 'log_file' => '/dev/null']); + + $http->on("start", function ($server) use ($pm) { + $pm->wakeup(); + }); + + $http->on("request", function (Request $request, Response $response) { + usleep(30000); + $response->end("Hello World\n".$request->get['code']); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +Close +Done diff --git a/tests/swoole_curl/select_cancel.phpt b/tests/swoole_curl/select_cancel.phpt new file mode 100644 index 00000000000..9e1b8352a73 --- /dev/null +++ b/tests/swoole_curl/select_cancel.phpt @@ -0,0 +1,96 @@ +--TEST-- +swoole_curl: select twice +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + run(function () use ($pm) { + $mh = curl_multi_init(); + + $add_handle = function ($url) use($mh) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_multi_add_handle($mh, $ch); + return $ch; + }; + + $ch1 = $add_handle("http://127.0.0.1:{$pm->getFreePort()}/"); + + $active = null; + + do { + $mrc = curl_multi_exec($mh, $active); + } while ($mrc == CURLM_CALL_MULTI_PERFORM); + + $now = microtime(true); + + $cid = Coroutine::getCid(); + go(function () use($cid) { + System::sleep(0.005); + Coroutine::cancel($cid); + }); + + while ($active && $mrc == CURLM_OK) { + $n = curl_multi_select($mh, TIMEOUT); + phpt_var_dump('return value:'.$n); + phpt_var_dump('swoole error:'.swoole_last_error()); + if ($n != -1) { + do { + $mrc = curl_multi_exec($mh, $active); + } while ($mrc == CURLM_CALL_MULTI_PERFORM); + } + if (Coroutine::isCanceled()) { + Assert::eq(swoole_last_error(), SWOOLE_ERROR_CO_CANCELED); + echo "CANCELED\n"; + break; + } + } + + Assert::eq(curl_multi_info_read($mh), false); + curl_multi_remove_handle($mh, $ch1); + curl_multi_close($mh); + }); + $pm->kill(); + echo "Done\n"; +}; +$pm->childFunc = function () use ($pm) { + $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(); + }); + $http->on("request", function (Request $request, Response $response) { + sleep(20000); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +CANCELED +Done diff --git a/tests/swoole_curl/select_timeout.phpt b/tests/swoole_curl/select_timeout.phpt new file mode 100644 index 00000000000..48cb8379b2e --- /dev/null +++ b/tests/swoole_curl/select_timeout.phpt @@ -0,0 +1,93 @@ +--TEST-- +swoole_curl: select timeout +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + run(function () use ($pm) { + $mh = curl_multi_init(); + + $add_handle = function ($url) use($mh) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_multi_add_handle($mh, $ch); + return $ch; + }; + + $ch1 = $add_handle("http://127.0.0.1:{$pm->getFreePort()}/"); + + $active = null; + + do { + $mrc = curl_multi_exec($mh, $active); + } while ($mrc == CURLM_CALL_MULTI_PERFORM); + + $now = microtime(true); + + while ($active && $mrc == CURLM_OK) { + $tm = microtime(true); + $n = curl_multi_select($mh, TIMEOUT); + Assert::lessThan(microtime(true) - $tm, TIMEOUT + 0.01); + + $error = swoole_last_error(); + phpt_var_dump('select return value: '.$n); + phpt_var_dump('swoole error: '.$error); + if ($n != -1) { + do { + $mrc = curl_multi_exec($mh, $active); + phpt_var_dump('exec return value: '.$mrc); + } while ($mrc == CURLM_CALL_MULTI_PERFORM); + } + if (microtime(true) - $now >= TIMEOUT * 4) { + echo "TIMEOUT\n"; + break; + } + } + + Assert::eq(curl_multi_info_read($mh), false); + curl_multi_remove_handle($mh, $ch1); + curl_multi_close($mh); + }); + $pm->kill(); + echo "Done\n"; +}; +$pm->childFunc = function () use ($pm) { + $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(); + }); + $http->on("request", function (Request $request, Response $response) { + sleep(20000); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +TIMEOUT +Done diff --git a/tests/swoole_curl/select_twice.phpt b/tests/swoole_curl/select_twice.phpt new file mode 100644 index 00000000000..35a4a9ecdc6 --- /dev/null +++ b/tests/swoole_curl/select_twice.phpt @@ -0,0 +1,32 @@ +--TEST-- +swoole_curl: select twice +--SKIPIF-- + +--FILE-- + true]); + echo "Done\n"; + }); + } +}); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: cURL is executing, cannot be operated in %s:%d +Stack trace: +#0 %s(%d): curl_multi_select(%s) +%A + thrown in %s on line %d diff --git a/tests/swoole_curl/setopt/1.phpt b/tests/swoole_curl/setopt/1.phpt new file mode 100644 index 00000000000..49423c926a0 --- /dev/null +++ b/tests/swoole_curl/setopt/1.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_curl/setopt: curl_setopt_array() function - tests setting multiple cURL options with curl_setopt_array() +--CREDITS-- +Mattijs Hoitink mattijshoitink@gmail.com +#Testfest Utrecht 2009 +--SKIPIF-- + +--FILE-- +run(function ($host) { + + // Use the set Environment variable + $url = "{$host}/get.php?test=get"; + +// Start the test + echo '== Starting test curl_setopt_array($ch, $options); ==' . "\n"; + +// curl handler + $ch = curl_init(); + +// options for the curl handler + $options = array( + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => 1 + ); + + + curl_setopt_array($ch, $options); + $returnContent = curl_exec($ch); + curl_close($ch); + + var_dump($returnContent); +}); + + +?> +--EXPECT-- +== Starting test curl_setopt_array($ch, $options); == +string(25) "Hello World! +Hello World!" diff --git a/tests/swoole_curl/setopt/3.phpt b/tests/swoole_curl/setopt/3.phpt new file mode 100644 index 00000000000..b62b7ea09c3 --- /dev/null +++ b/tests/swoole_curl/setopt/3.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_curl/setopt: curl_setopt() call with CURLOPT_HTTPHEADER +--CREDITS-- +Paul Sohier +#phptestfest utrecht +--SKIPIF-- + +--FILE-- +disableNativeCurl(); +$cm->run(function ($host) { + // start testing + echo "*** curl_setopt() call with CURLOPT_HTTPHEADER\n"; + + $url = "{$host}/"; + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_HTTPHEADER, 1); + + $curl_content = curl_exec($ch); + curl_close($ch); + + var_dump( $curl_content ); + + $ch = curl_init(); + + ob_start(); // start output buffering + curl_setopt($ch, CURLOPT_HTTPHEADER, array()); + curl_setopt($ch, CURLOPT_URL, $host); + + $curl_content = curl_exec($ch); + ob_end_clean(); + curl_close($ch); + + var_dump( $curl_content ); + +}); + + +?> +--EXPECTF-- +*** curl_setopt() call with CURLOPT_HTTPHEADER + +Warning: swoole_curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument in %s on line %d +bool(false) +bool(true) diff --git a/tests/swoole_curl/setopt/4.phpt b/tests/swoole_curl/setopt/4.phpt new file mode 100644 index 00000000000..873ff53e3f4 --- /dev/null +++ b/tests/swoole_curl/setopt/4.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_curl/setopt: curl_setopt() call with CURLOPT_RETURNTRANSFER +--CREDITS-- +Paul Sohier +#phptestfest utrecht +--SKIPIF-- + +--FILE-- +run(function ($host) { + +// start testing + echo "*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 1\n"; + + $url = "{$host}/"; + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_URL, $url); + + $curl_content = curl_exec($ch); + curl_close($ch); + + var_dump( $curl_content ); + + echo "*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 0\n"; + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0); + curl_setopt($ch, CURLOPT_URL, $url); + ob_start(); + $curl_content = curl_exec($ch); + ob_end_clean(); + curl_close($ch); + + var_dump( $curl_content ); + +}); + +?> +--EXPECTF-- +*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 1 +string(%d) "%a" +*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 0 +bool(true) diff --git a/tests/swoole_library/curl/setopt/5_skip.phpt b/tests/swoole_curl/setopt/5_skip.phpt similarity index 89% rename from tests/swoole_library/curl/setopt/5_skip.phpt rename to tests/swoole_curl/setopt/5_skip.phpt index e15c5eaeb3e..2ac4cf19a37 100644 --- a/tests/swoole_library/curl/setopt/5_skip.phpt +++ b/tests/swoole_curl/setopt/5_skip.phpt @@ -1,16 +1,16 @@ --TEST-- -swoole_library/curl/setopt: cURL option CURLOPT_READFUNCTION +swoole_curl/setopt: cURL option CURLOPT_READFUNCTION --CREDITS-- WHITE new media architects - Jeroen Vermeulen #testfest Utrecht 2009 --SKIPIF-- --FILE-- +--FILE-- +run(function ($host) { + + $ch = curl_init(); + + $options = array( + CURLOPT_URL => 'https://static.zhihu.com/heifetz/chunks/5946.4600cc0c1b3dcecac17c.js', + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_FILETIME => true, + CURLOPT_NOBODY => true, + ); + + curl_setopt_array($ch, $options); + curl_exec($ch); + $info = curl_getinfo($ch); + Assert::assert(!empty($info['filetime'])); + Assert::greaterThanEq($info['filetime'], 1000000); + curl_close($ch); + echo "END\n"; +}, false); + +?> +--EXPECT-- +END diff --git a/tests/swoole_library/curl/setopt/filetime_2.phpt b/tests/swoole_curl/setopt/filetime_2.phpt similarity index 75% rename from tests/swoole_library/curl/setopt/filetime_2.phpt rename to tests/swoole_curl/setopt/filetime_2.phpt index a786c823d1d..5879c4385af 100644 --- a/tests/swoole_library/curl/setopt/filetime_2.phpt +++ b/tests/swoole_curl/setopt/filetime_2.phpt @@ -1,10 +1,10 @@ --TEST-- -swoole_library/curl/setopt: CURLOPT_FILETIME [return -1] +swoole_curl/setopt: CURLOPT_FILETIME [return -1] --SKIPIF-- - + --FILE-- run(function ($host) { diff --git a/tests/swoole_library/curl/setopt/header_out.phpt b/tests/swoole_curl/setopt/header_out.phpt similarity index 84% rename from tests/swoole_library/curl/setopt/header_out.phpt rename to tests/swoole_curl/setopt/header_out.phpt index 565fbc422cb..1ec1969c8ac 100644 --- a/tests/swoole_library/curl/setopt/header_out.phpt +++ b/tests/swoole_curl/setopt/header_out.phpt @@ -1,7 +1,7 @@ --TEST-- -swoole_library/curl/setopt: CURLINFO_HEADER_OUT +swoole_curl/setopt: CURLINFO_HEADER_OUT --SKIPIF-- - + --FILE-- run(function ($host) { diff --git a/tests/swoole_library/curl/setopt/infile.phpt b/tests/swoole_curl/setopt/infile.phpt similarity index 85% rename from tests/swoole_library/curl/setopt/infile.phpt rename to tests/swoole_curl/setopt/infile.phpt index 0916189f36c..5dd86c05d39 100644 --- a/tests/swoole_library/curl/setopt/infile.phpt +++ b/tests/swoole_curl/setopt/infile.phpt @@ -1,10 +1,10 @@ --TEST-- -swoole_library/curl/setopt: CURLOPT_INFILE +swoole_curl/setopt: CURLOPT_INFILE --SKIPIF-- - + --FILE-- run(function ($host) { diff --git a/tests/swoole_library/curl/setopt/nobody.phpt b/tests/swoole_curl/setopt/nobody.phpt similarity index 81% rename from tests/swoole_library/curl/setopt/nobody.phpt rename to tests/swoole_curl/setopt/nobody.phpt index 46a7e6c632e..d8dd27d001b 100644 --- a/tests/swoole_library/curl/setopt/nobody.phpt +++ b/tests/swoole_curl/setopt/nobody.phpt @@ -1,7 +1,7 @@ --TEST-- -swoole_library/curl/setopt: CURLOPT_NOBODY +swoole_curl/setopt: CURLOPT_NOBODY --SKIPIF-- - + --FILE-- run(function ($host) { 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 new file mode 100644 index 00000000000..b8b224de151 --- /dev/null +++ b/tests/swoole_curl/sleep.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_curl: sleep +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); + $s = microtime(true); + run(function () use ($pm) { + $n = N; + while($n--) { + go(function() use ($pm) { + $ch = curl_init(); + $code = uniqid('swoole_'); + $url = "http://127.0.0.1:".$pm->getFreePort()."/?code=".urlencode($code); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) { + return strlen($strHeader); + }); + + $output = curl_exec($ch); + Assert::eq($output, "Hello World\n".$code); + if ($output === false) { + echo "CURL Error:" . curl_error($ch); + } + curl_close($ch); + }); + } + }); + Assert::lessThan(microtime(true) - $s, 0.5); + $pm->kill(); + echo "Done\n"; +}; +$pm->childFunc = function () use ($pm) { + $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) { + $pm->wakeup(); + }); + + $http->on("request", function (Request $request, Response $response) { + usleep(300000); + $response->end("Hello World\n".$request->get['code']); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/ssl/version.phpt b/tests/swoole_curl/ssl/version.phpt new file mode 100644 index 00000000000..fdeb18c6e31 --- /dev/null +++ b/tests/swoole_curl/ssl/version.phpt @@ -0,0 +1,28 @@ +--TEST-- +swoole_curl/ssl: Test SSL_VERSION option +--SKIPIF-- + + +--FILE-- +run(function ($host) { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + 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.baidu.com/"); + $result = curl_exec($ch); + Assert::assert($result); + Assert::contains($result, '百度'); + curl_close($ch); + +}, false); + +?> +--EXPECTF-- 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/symfony.phpt b/tests/swoole_curl/symfony.phpt new file mode 100644 index 00000000000..9f69132267a --- /dev/null +++ b/tests/swoole_curl/symfony.phpt @@ -0,0 +1,45 @@ +--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; +}); + +Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); +?> +--EXPECT-- +Done diff --git a/tests/swoole_curl/template b/tests/swoole_curl/template new file mode 100644 index 00000000000..f5cc982b0fd --- /dev/null +++ b/tests/swoole_curl/template @@ -0,0 +1,7 @@ +require __DIR__ . '/../../include/bootstrap.php'; + +$cm = new \SwooleTest\CurlManager(); +$cm->run(function ($host) { + + +}); \ No newline at end of file diff --git a/tests/swoole_curl/timer_coredump.phpt b/tests/swoole_curl/timer_coredump.phpt new file mode 100644 index 00000000000..89639847cdf --- /dev/null +++ b/tests/swoole_curl/timer_coredump.phpt @@ -0,0 +1,34 @@ +--TEST-- +swoole_curl: timer coredump +--SKIPIF-- + +--FILE-- + +--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 new file mode 100644 index 00000000000..149238dce1d --- /dev/null +++ b/tests/swoole_curl/upload/1.phpt @@ -0,0 +1,89 @@ +--TEST-- +swoole_curl/upload: CURL file uploading +--INI-- +--SKIPIF-- + +--FILE-- +run(function ($host) { + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "{$host}/get.php?test=file"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + + testcurl($ch, __DIR__ . '/curl_testdata1.txt'); + testcurl($ch, __DIR__ . '/curl_testdata1.txt', 'text/plain'); + testcurl($ch, __DIR__ . '/curl_testdata1.txt', '', 'foo.txt'); + testcurl($ch, __DIR__ . '/curl_testdata1.txt', 'text/plain', 'foo.txt'); + + $file = new CurlFile(__DIR__ . '/curl_testdata1.txt'); + $file->setMimeType('text/plain'); + var_dump($file->getMimeType()); + var_dump($file->getFilename()); + curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); + var_dump(curl_exec($ch)); + + $file = curl_file_create(__DIR__ . '/curl_testdata1.txt'); + $file->setPostFilename('foo.txt'); + var_dump($file->getPostFilename()); + curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); + var_dump(curl_exec($ch)); + + 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)); + + curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true); + $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); + curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + var_dump(curl_exec($ch)); + + curl_setopt($ch, CURLOPT_URL, "{$host}/get.php?test=post"); + $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); + curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + var_dump(curl_exec($ch)); + + curl_close($ch); +}); + +function testcurl($ch, $name, $mime = '', $postname = '') +{ + if(!empty($postname)) { + $file = new CurlFile($name, $mime, $postname); + } else if(!empty($mime)) { + $file = new CurlFile($name, $mime); + } else { + $file = new CurlFile($name); + } + curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); + $ret = curl_exec($ch) or die("ERROR: Code=".curl_errno($ch). "Msg=".curl_error($ch)."\n"); + var_dump($ret); +} +?> +--EXPECTF-- +string(%d) "curl_testdata1.txt|application/octet-stream|6" +string(%d) "curl_testdata1.txt|text/plain|6" +string(%d) "foo.txt|application/octet-stream|6" +string(%d) "foo.txt|text/plain|6" +string(%d) "text/plain" +string(%d) "%s/curl_testdata1.txt" +string(%d) "curl_testdata1.txt|text/plain|6" +string(%d) "foo.txt" +string(%d) "foo.txt|application/octet-stream|6" + +Warning: %s(): Disabling safe uploads is no longer supported in %s on line %d +string(0) "" +string(0) "" +string(%d) "array(1) { + ["file"]=> + string(%d) "@%s/curl_testdata1.txt" +} +" 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_library/curl/upload/curl_testdata1.txt b/tests/swoole_curl/upload/curl_testdata1.txt similarity index 100% rename from tests/swoole_library/curl/upload/curl_testdata1.txt rename to tests/swoole_curl/upload/curl_testdata1.txt diff --git a/tests/swoole_library/curl/upload/curl_testdata2.txt b/tests/swoole_curl/upload/curl_testdata2.txt similarity index 100% rename from tests/swoole_library/curl/upload/curl_testdata2.txt rename to tests/swoole_curl/upload/curl_testdata2.txt diff --git a/tests/swoole_curl/yield_in_callback.phpt b/tests/swoole_curl/yield_in_callback.phpt new file mode 100644 index 00000000000..01df11fbee3 --- /dev/null +++ b/tests/swoole_curl/yield_in_callback.phpt @@ -0,0 +1,51 @@ +--TEST-- +swoole_curl: suspend in callback +--SKIPIF-- + +--FILE-- + +--EXPECT-- +exec +exec end +Close 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/deprecated_event_wait.phpt b/tests/swoole_event/deprecated_event_wait.phpt new file mode 100644 index 00000000000..ab49ff4a27e --- /dev/null +++ b/tests/swoole_event/deprecated_event_wait.phpt @@ -0,0 +1,22 @@ +--TEST-- +swoole_event: deprecated_event_wait +--SKIPIF-- + +--FILE-- + +--EXPECT-- 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/sync_client_1.phpt b/tests/swoole_event/sync_client_1.phpt new file mode 100644 index 00000000000..bd06c58ef7d --- /dev/null +++ b/tests/swoole_event/sync_client_1.phpt @@ -0,0 +1,35 @@ +--TEST-- +swoole_event: sync client +--SKIPIF-- + +--FILE-- + false]); + +$fp = new Client(SWOOLE_SOCK_TCP); + +// sync connect +$fp->connect('www.qq.com', 80); + +Event::add($fp, function($fp) { + $resp = $fp->recv(8192); + Assert::contains($resp, 'Location: https://www.qq.com/'); + + Event::del($fp); + $fp->close(); + + echo "Done\n"; +}); + +Event::write($fp, "GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); +Event::wait(); +?> +--EXPECT-- +Done diff --git a/tests/swoole_event/sync_client_2.phpt b/tests/swoole_event/sync_client_2.phpt new file mode 100644 index 00000000000..ce1da3a0121 --- /dev/null +++ b/tests/swoole_event/sync_client_2.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_event: sync client +--SKIPIF-- + +--FILE-- + false]); + +$fp = new Client(SWOOLE_SOCK_TCP); +// async connect +$result = $fp->connect('www.qq.com', 80, 1, 1); + +Assert::true($result); +Assert::eq($fp->errCode, SOCKET_EINPROGRESS); + +Event::add($fp, null, function (Client $fp) { + $fp->send("GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n"); + Event::set($fp, function ($fp) { + $resp = $fp->recv(8192); + Assert::contains($resp, 'Location: https://www.qq.com/'); + + Event::del($fp); + $fp->close(); + + echo "Done\n"; + }, null, SWOOLE_EVENT_READ); +}, SWOOLE_EVENT_WRITE); + +Event::wait(); +?> +--EXPECT-- +Done 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 new file mode 100644 index 00000000000..88ff09e8a2f --- /dev/null +++ b/tests/swoole_http2_client_coro/connect_twice.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_http2_client_coro: connect twice +--SKIPIF-- + +--FILE-- +connect(); + $req = new Request(); + $uuid = uniqid(); + $req->method = 'GET'; + $req->path = '/base64/' . base64_encode($uuid); + $client->send($req); + $chan->push(true); + $resp = $client->recv(); + Assert::eq($resp->statusCode, 200); + Assert::eq($resp->data, $uuid); + $chan->pop(); + }); + go(function () use ($client, $chan) { + Assert::eq($client->connect(), false); + $uuid = uniqid(); + $req = new Request(); + $req->method = 'GET'; + $req->path = '/base64/' . base64_encode($uuid); + $client->send($req); + $chan->push(true); + Assert::eq($client->recv(), false); + $chan->pop(); + }); +}); + +?> +--EXPECT-- diff --git a/tests/swoole_http2_client_coro/error.phpt b/tests/swoole_http2_client_coro/error.phpt index 366e1b977be..1e2aab2bb0d 100644 --- a/tests/swoole_http2_client_coro/error.phpt +++ b/tests/swoole_http2_client_coro/error.phpt @@ -11,9 +11,9 @@ go(function () { Assert::false($cli->connect()); Assert::same($cli->errCode, SOCKET_ETIMEDOUT); Assert::false($cli->send(new Swoole\Http2\Request)); - Assert::same($cli->errCode, SOCKET_ECONNRESET); + Assert::same($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); Assert::false($cli->recv(1)); - Assert::same($cli->errCode, SOCKET_ECONNRESET); + Assert::same($cli->errCode, SWOOLE_ERROR_CLIENT_NO_CONNECTION); }); Swoole\Event::wait(); ?> 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 new file mode 100644 index 00000000000..4f7ff14e5c5 --- /dev/null +++ b/tests/swoole_http2_server/http2_headers.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_http2_server: array headers +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $output = `curl --http2-prior-knowledge --silent -I http://127.0.0.1:{$pm->getFreePort()}`; + 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->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) { + $response->header('test-value', [ + "a\r\n", + 'd5678', + "e \n ", + null, + 5678, + 3.1415926, + ]); + $response->end("

Hello Swoole.

"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--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_twice.phpt b/tests/swoole_http_client_coro/get_twice.phpt index 1b7a9faec65..ce7063835eb 100644 --- a/tests/swoole_http_client_coro/get_twice.phpt +++ b/tests/swoole_http_client_coro/get_twice.phpt @@ -11,11 +11,11 @@ use Swoole\Coroutine\Http\Client; const N = 2; Swoole\Coroutine\Run(function () { - $client = new Client('www.zhe800.com', 443, true); + $client = new Client('www.baidu.com', 443, true); $client->set(['timeout' => 5,]); for ($i = 0; $i < N; $i++) { $rand = mt_rand(100000, 999999999); - $path = "/email_subscribe?email=" . $rand . "@" . substr(md5(microtime(true)), 0, 8) . ".com"; + $path = "/index.php?email=" . $rand . "@" . substr(md5(microtime(true)), 0, 8) . ".com"; $result = $client->get($path); if (!$result) { var_dump("ERROR: ".$client->getStatusCode()); diff --git a/tests/swoole_http_client_coro/get_twice_keepalive.phpt b/tests/swoole_http_client_coro/get_twice_keepalive.phpt index afed15f6fa8..963f09ec22f 100644 --- a/tests/swoole_http_client_coro/get_twice_keepalive.phpt +++ b/tests/swoole_http_client_coro/get_twice_keepalive.phpt @@ -11,11 +11,11 @@ use Swoole\Coroutine\Http\Client; const N = 2; Swoole\Coroutine\Run(function () { - $client = new Client('www.zhe800.com', 443, true); + $client = new Client('www.baidu.com', 443, true); $client->set(['timeout' => 5,]); for ($i = 0; $i < N; $i++) { $rand = mt_rand(100000, 999999999); - $path = "/email_subscribe?email=" . $rand . "@" . substr(md5(microtime(true)), 0, 8) . ".com"; + $path = "/index.php?email=" . $rand . "@" . substr(md5(microtime(true)), 0, 8) . ".com"; Assert::assert($client->get($path)); Assert::assert($client->getStatusCode() == 200); } 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 new file mode 100644 index 00000000000..69018dd957b --- /dev/null +++ b/tests/swoole_http_client_coro/http_proxy_with_host_port.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_http_client_coro: http client with http_proxy and host and port +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', 1234); + $cli->set([ + 'timeout' => 30, + 'http_proxy_host' => '127.0.0.1', + 'http_proxy_port' => $pm->getFreePort(), + ]); + $cli->setHeaders([ + 'Host' => '127.0.0.1:1234', + ]); + $cli->get('/'); + $pm->kill(); + }); +}; + +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_BASE); + $server->set([ + 'log_file' => '/dev/null', + 'open_eof_check' => true, + 'package_eof' => "\r\n\r\n", + ]); + $server->on('Receive', function ($server, $fd, $reactor_id, $data) { + echo $data; + $server->close($fd); + }); + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); + +?> +--EXPECT-- +GET http://127.0.0.1:1234/ HTTP/1.1 +Host: 127.0.0.1:1234 +Connection: keep-alive +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 eca56c80013..07216de64ba 100644 --- a/tests/swoole_http_client_coro/websocket/bug_01.phpt +++ b/tests/swoole_http_client_coro/websocket/bug_01.phpt @@ -4,36 +4,34 @@ swoole_http_client_coro/websocket: handshake + frame --FILE-- 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 new file mode 100644 index 00000000000..0970e441ef6 --- /dev/null +++ b/tests/swoole_http_server/0.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_http_server: basic functions +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm, $html) { + go(function () use ($pm, $html) { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); + Assert::same($data, $html); + $pm->kill(); + }); + Swoole\Event::wait(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm, $html) { + $serv = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv->set([ + 'log_file' => '/dev/null', + ]); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('request', function ($req, $resp) use ($html) { + Assert::true($resp->isWritable()); + $resp->end($html); + Assert::false($resp->isWritable()); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 9d391771953..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)); }); @@ -40,5 +40,5 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -[%s] WARNING finish (ERRNO %d): The length of data [%d] exceeds the output buffer size[%d], please use the sendfile, chunked transfer mode or adjust the output_buffer_size +[%s] WARNING ProcessFactory::finish() (ERRNO %d): The length of data [%d] exceeds the output buffer size[%d], please use the sendfile, chunked transfer mode or adjust the output_buffer_size DONE 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_2947.phpt b/tests/swoole_http_server/bug_2947.phpt index b10af32e9e1..302849846de 100644 --- a/tests/swoole_http_server/bug_2947.phpt +++ b/tests/swoole_http_server/bug_2947.phpt @@ -37,7 +37,7 @@ $pm->childFunc = function () use ($pm) { $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { Assert::same($request->server['request_method'], 'POST'); Assert::same($request->rawContent(), $pm->getRandomData()); - $response->end('OK'); + $response->end(str_repeat('OK', 16)); }); $http->start(); }; 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 new file mode 100644 index 00000000000..25bf5912665 --- /dev/null +++ b/tests/swoole_http_server/compression_min_length.phpt @@ -0,0 +1,45 @@ +--TEST-- +swoole_http_server: compression_min_length +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) +{ + run(function () use ($pm) { + $cli = new Client('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['Accept-Encoding' => 'gzip', ]); + $cli->get('/?bytes=128'); + Assert::eq($cli->getHeaders()['content-encoding'], 'gzip'); + + $cli = new Client('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['Accept-Encoding' => 'gzip', ]); + $cli->get('/?bytes=127'); + Assert::assert(!isset($cli->getHeaders()['content-encoding'])); + }); + echo "DONE\n"; + $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(['compression_min_length' => 128,]); + $http->on("WorkerStart", function ($serv, $wid) { + global $pm; + $pm->wakeup(); + }); + $http->on("request", function ($request, Swoole\Http\Response $response) { + $response->end(str_repeat('A', $request->get['bytes'])); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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/create_request.phpt b/tests/swoole_http_server/create_request.phpt new file mode 100644 index 00000000000..9b3e4c17bf0 --- /dev/null +++ b/tests/swoole_http_server/create_request.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_http_server: parse request +--SKIPIF-- + +--FILE-- +header, 0); +Assert::false($req->isCompleted()); + +$data1 = substr($data, 0, rand(100, 600)); +$data2 = substr($data, strlen($data1)); + +Assert::eq($req->parse($data1), strlen($data1)); +Assert::false($req->isCompleted()); +Assert::eq($req->parse($data2), strlen($data2)); +Assert::false($req->isCompleted()); +Assert::eq($req->parse("\r\n"), 2); + +Assert::true($req->isCompleted()); +Assert::false($req->parse('error data')); + +Assert::eq("GET", $req->getMethod()); + +Assert::greaterThan(count($req->header), 4); +Assert::eq(count($req->cookie), 3); + +Assert::eq($req->getData(), $data."\r\n"); + +$req2 = Request::create(['parse_cookie' => false]); +Assert::eq($req2->parse($data . "\r\n"), strlen($data) + 2); +Assert::null($req2->cookie); + +$data = "POST /index.html?hello=world&test=2123 HTTP/1.1\r\n"; +$data .= "Host: 127.0.0.1\r\n"; +$data .= "Connection: keep-alive\r\n"; +$data .= "Pragma: no-cache\r\n"; +$data .= "Cache-Control: no-cache\r\n"; +$data .= "Upgrade-Insecure-Requests: \r\n"; +$data .= "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36\r\n"; +$data .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n"; +$data .= "Accept-Encoding: gzip, deflate, br\r\n"; +$data .= "Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7,ja;q=0.6\r\n"; +$data .= "Cookie: env=pretest; phpsessid=fcccs2af8673a2f343a61a96551c8523d79ea; username=hantianfeng\r\n"; + +$req3 = Request::create(); +$req3->parse($data); +Assert::eq("POST", $req3->getMethod()); + +?> +--EXPECT-- 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 663a5a3e97e..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,8 +28,9 @@ $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) { - $response->header("content-length", "10000 "); + $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", [ "a\r\n", "b1234 ", @@ -39,7 +40,7 @@ $pm->childFunc = function () use ($pm) { 5678, 3.1415926, ]); - $response->end("hello world"); + $response->end($msg); }); $http->start(); }; @@ -49,6 +50,7 @@ $pm->run(); ?> --EXPECTF-- HTTP/1.1 200 OK +Content-Length: 11 Test-Value: a Test-Value: b1234 Test-Value: d5678 @@ -56,9 +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 -Content-Length: 11 -hello world \ No newline at end of file +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 new file mode 100644 index 00000000000..914995ddb9d --- /dev/null +++ b/tests/swoole_http_server/error_1203.phpt @@ -0,0 +1,50 @@ +--TEST-- +swoole_http_server: http_compression +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) +{ + go(function () use ($pm) { + try { + $data = httpGetBody("http://127.0.0.1:{$pm->getFreePort()}/"); + } catch(Exception $e) { + Assert::contains($e->getMessage(), 'Connection reset by peer'); + } + $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_PROCESS); + + $http->set([ + 'http_compression' => false, + 'log_file' => '/dev/null', + 'buffer_output_size' => 128 * 1024, + ]); + + $http->on("WorkerStart", function ($serv, $wid) use ($pm) { + $pm->wakeup(); + }); + + $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); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 new file mode 100644 index 00000000000..c8cb1556108 --- /dev/null +++ b/tests/swoole_http_server/sendfile_no_keepalive.phpt @@ -0,0 +1,55 @@ +--TEST-- +swoole_http_server: sendfile with dispatch_mode=7 +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $n = MAX_CONCURRENCY_LOW; + while ($n--) { + go(function () use ($pm) { + $cli = new Client('127.0.0.1', $pm->getFreePort()); + $cli->setHeaders(['KeepAlive' => 'off', 'Connection' => 'close']); + for ($i = MAX_REQUESTS_LOW; $i--;) { + $cli->get('/'); + Assert::contains($cli->getBody(), 'swoole_http_server: sendfile with dispatch_mode=7'); + } + }); + } + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $http->set([ + 'log_file' => '/dev/null', + 'dispatch_mode' => 7, + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->sendfile(__FILE__); + }); + + $http->on('message', function(){}); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt b/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt new file mode 100644 index 00000000000..44d76ed4fc6 --- /dev/null +++ b/tests/swoole_http_server/sendfile_with_dispatch_mode_7.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_http_server: sendfile with dispatch_mode=7 +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $n = MAX_CONCURRENCY_LOW; + while ($n--) { + go(function () use ($pm) { + $cli = new Client('127.0.0.1', $pm->getFreePort()); + for ($i = MAX_REQUESTS_LOW; $i--;) { + $cli->get('/'); + Assert::contains($cli->getBody(), 'swoole_http_server: sendfile with dispatch_mode=7'); + } + }); + } + }); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP); + $http->set([ + 'log_file' => '/dev/null', + 'dispatch_mode' => 7, + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) { + $response->sendfile(__FILE__); + }); + + $http->on('message', function(){}); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/sendfile_with_ssl.phpt b/tests/swoole_http_server/sendfile_with_ssl.phpt new file mode 100644 index 00000000000..95cbb374650 --- /dev/null +++ b/tests/swoole_http_server/sendfile_with_ssl.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_http_server: sendfile with ssl +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + for ($i = MAX_REQUESTS; $i--;) { + $send_file = get_safe_random(mt_rand(0, 65535 * 10)); + file_put_contents('/tmp/sendfile.txt', $send_file); + + $ctxArr = [ + 'verify_peer' => false, + ]; + $ctx = stream_context_create(['ssl' => $ctxArr]); + $recv_file = file_get_contents("https://127.0.0.1:{$pm->getFreePort()}", false, $ctx); + Assert::same($send_file, $recv_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, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $http->set([ + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'ssl_cert_file' => SSL_FILE_DIR . '/server.crt', + '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) { + $response->header('Content-Type', 'application/octet-stream'); + $response->header('Content-Disposition', 'attachment; filename=recvfile.txt'); + $response->sendfile('/tmp/sendfile.txt'); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_http_server/set_content_length.phpt b/tests/swoole_http_server/set_content_length.phpt new file mode 100644 index 00000000000..e1d73162ce3 --- /dev/null +++ b/tests/swoole_http_server/set_content_length.phpt @@ -0,0 +1,44 @@ +--TEST-- +swoole_http_server: allow setting content length header +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm, $data) { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://127.0.0.1:" . $pm->getFreePort() . '/'); + curl_setopt($ch, CURLOPT_HEADER, 1); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $response = curl_exec($ch); + $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $header = substr($response, 0, $header_size); + Assert::assert(strrpos($header, 'Content-Length: 50') > 0); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $data) { + $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 ($req, Swoole\Http\Response $resp) use ($data) { + $resp->header('Content-Type', 'application/json'); + $resp->header('Content-Length', 50); + $resp->end($data); + }); + + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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_min_length.phpt b/tests/swoole_http_server_coro/compression_min_length.phpt new file mode 100644 index 00000000000..b3acd07c8a6 --- /dev/null +++ b/tests/swoole_http_server_coro/compression_min_length.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_http_server_coro: compression_min_length +--SKIPIF-- + +--FILE-- +set(['compression_min_length' => 128,]); + $server->handle('/test', function ($request, $response) { + $response->end(str_repeat('A', $request->get['bytes'])); + }); + $server->handle('/shutdown', function ($request, $response) use ($server) { + $response->end("shutdown"); + $server->shutdown(); + }); + $server->start(); + }); + + go(function () { + $cli = new Client('127.0.0.1', TEST_PORT); + $cli->setHeaders(['Accept-Encoding' => 'gzip', ]); + $cli->get('/test?bytes=128'); + Assert::eq($cli->getHeaders()['content-encoding'], 'gzip'); + + $cli = new Client('127.0.0.1', TEST_PORT); + $cli->setHeaders(['Accept-Encoding' => 'gzip', ]); + $cli->get('/test?bytes=127'); + Assert::assert(!isset($cli->getHeaders()['content-encoding'])); + + file_get_contents('http://127.0.0.1:' . TEST_PORT . '/shutdown'); + }); +}); +echo "DONE\n"; +?> +--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/create_response.phpt b/tests/swoole_http_server_coro/create_response.phpt new file mode 100644 index 00000000000..e724549c526 --- /dev/null +++ b/tests/swoole_http_server_coro/create_response.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_http_server_coro: create response +--SKIPIF-- + +--FILE-- +handle(function (Connection $conn) use ($server) { + $req = Request::create(); + while(true) { + $data = $conn->recv(); + if (strlen($data) != $req->parse($data) or $req->isCompleted()) { + break; + } + } + Assert::eq($req->get['value'], 1); + Assert::eq($req->get['hello'], 'world'); + $resp = Response::create($conn->exportSocket()); + $resp->header('X-Server', 'swoole'); + $resp->end(GREETER); + + $server->shutdown(); + }); + $server->start(); + }); + + go(function () use ($port, $server) { + $httpClient = new Co\Http\Client(HTTP_SERVER_HOST, $port, false); + $httpClient->setMethod("POST"); + $httpClient->setData("HELLO"); + $ok = $httpClient->execute("/rawcookie?hello=world&value=1"); + Assert::assert($ok); + Assert::same($httpClient->statusCode, 200); + Assert::same($httpClient->errCode, 0); + Assert::eq($httpClient->getHeaders()['x-server'], 'swoole'); + Assert::same($httpClient->getBody(), GREETER); + $server->shutdown(); + }); +}); +?> +--EXPECT-- diff --git a/tests/swoole_http_server_coro/create_response_2.phpt b/tests/swoole_http_server_coro/create_response_2.phpt new file mode 100644 index 00000000000..398b546cd1d --- /dev/null +++ b/tests/swoole_http_server_coro/create_response_2.phpt @@ -0,0 +1,58 @@ +--TEST-- +swoole_http_server_coro: create response [2] +--SKIPIF-- + +--FILE-- +handle(function (Connection $conn) use ($server) { + $req = Request::create(); + while(true) { + $data = $conn->recv(); + if (strlen($data) != $req->parse($data) or $req->isCompleted()) { + break; + } + } + Assert::eq($req->get['value'], 1); + Assert::eq($req->get['hello'], 'world'); + $resp = Response::create([$conn->exportSocket(), $req]); + $resp->header('X-Server', 'swoole'); + $resp->end(GREETER); + + $server->shutdown(); + }); + $server->start(); + }); + + go(function () use ($port, $server) { + $httpClient = new Co\Http\Client(HTTP_SERVER_HOST, $port, false); + $httpClient->setMethod("POST"); + $httpClient->setData("HELLO"); + $ok = $httpClient->execute("/rawcookie?hello=world&value=1"); + Assert::assert($ok); + Assert::same($httpClient->statusCode, 200); + Assert::same($httpClient->errCode, 0); + Assert::eq($httpClient->getHeaders()['x-server'], 'swoole'); + Assert::same($httpClient->getBody(), GREETER); + $server->shutdown(); + }); +}); +?> +--EXPECT-- 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 7db2e512c0e..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'); @@ -46,5 +45,5 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -[%s] WARNING ssl_accept: bad SSL client[127.0.0.1:%d], reason=%d, error_string=%s +[%s] WARNING Socket::ssl_accept(): bad SSL client[127.0.0.1:%d], reason=%d, error_string=%s

Stop

diff --git a/tests/swoole_http_server_coro/tcp_nodelay.phpt b/tests/swoole_http_server_coro/tcp_nodelay.phpt index 55528c22dfa..ccd7e670abf 100644 --- a/tests/swoole_http_server_coro/tcp_nodelay.phpt +++ b/tests/swoole_http_server_coro/tcp_nodelay.phpt @@ -25,7 +25,7 @@ go(function () use ($port) { echo httpGetBody("http://127.0.0.1:{$port}/") . PHP_EOL; echo httpGetBody("http://127.0.0.1:{$port}/stop?hello=1") . PHP_EOL; }); -swoole_event_wait(); +Swoole\Event::wait(); ?> --EXPECT-- diff --git a/tests/swoole_http_server_coro/upload.phpt b/tests/swoole_http_server_coro/upload.phpt index 3e94c2bd303..b815236cb60 100644 --- a/tests/swoole_http_server_coro/upload.phpt +++ b/tests/swoole_http_server_coro/upload.phpt @@ -52,7 +52,7 @@ $pm->childFunc = function () use ($pm) { $server->start(); }); $pm->wakeup(); - swoole_event::wait(); + Swoole\Event::wait(); }; $pm->childFirst(); diff --git a/tests/swoole_http_server_coro/websocket.phpt b/tests/swoole_http_server_coro/websocket.phpt index 46f3eca029e..4f50b2ed708 100644 --- a/tests/swoole_http_server_coro/websocket.phpt +++ b/tests/swoole_http_server_coro/websocket.phpt @@ -15,18 +15,20 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { $cli->set(['timeout' => 5]); $ret = $cli->upgrade('/websocket'); Assert::assert($ret); - $data = sha1(get_safe_random(mt_rand(0, 1024))); + $data = sha1(get_safe_random(mt_rand(1, 1024))); for ($n = MAX_REQUESTS; $n--;) { $cli->push($data); $ret = $cli->recv(); Assert::same($ret->data, "Hello {$data}!"); $ret = $cli->recv(); Assert::same($ret->data, "How are you, {$data}?"); + Assert::same($cli->cookies['test-file'], __FILE__); + Assert::same($cli->headers['x-swoole'], 'hello'); $count++; } }); } - swoole_event_wait(); + Swoole\Event::wait(); Assert::same($count, (MAX_CONCURRENCY * MAX_REQUESTS)); $pm->kill(); }; @@ -34,6 +36,8 @@ $pm->childFunc = function () use ($pm) { go(function () use ($pm) { $server = new Co\Http\Server("127.0.0.1", $pm->getFreePort(), false); $server->handle('/websocket', function ($request, $ws) { + $ws->header('x-swoole', 'hello'); + $ws->cookie('test-file', __FILE__); $ws->upgrade(); while (true) { $frame = $ws->recv(); @@ -57,7 +61,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/websocket_close.phpt b/tests/swoole_http_server_coro/websocket_close.phpt index 8d187e5104a..2e9cbbd80db 100644 --- a/tests/swoole_http_server_coro/websocket_close.phpt +++ b/tests/swoole_http_server_coro/websocket_close.phpt @@ -25,7 +25,7 @@ $pm->parentFunc = function (int $pid) use ($pm, &$count) { Assert::lessThan(microtime(true) - $s, 0.002); Assert::same($ret, false); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { @@ -53,7 +53,7 @@ $pm->childFunc = function () use ($pm) { $pm->wakeup(); $server->start(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_http_server_coro/websocket_ping_pong.phpt b/tests/swoole_http_server_coro/websocket_ping_pong.phpt index 6a1703fe8dd..4c68cf64c92 100644 --- a/tests/swoole_http_server_coro/websocket_ping_pong.phpt +++ b/tests/swoole_http_server_coro/websocket_ping_pong.phpt @@ -29,7 +29,7 @@ $pm->parentFunc = function () use ($pm) { $ret = $cli->recv(); Assert::same($ret->opcode, WEBSOCKET_OPCODE_PONG); }); - swoole_event_wait(); + Swoole\Event::wait(); $pm->kill(); }; $pm->childFunc = function () use ($pm) { @@ -62,7 +62,7 @@ $pm->childFunc = function () use ($pm) { $server->start(); $pm->wakeup(); }); - swoole_event_wait(); + Swoole\Event::wait(); }; $pm->childFirst(); $pm->run(); diff --git a/tests/swoole_iouring/mix.phpt b/tests/swoole_iouring/mix.phpt new file mode 100644 index 00000000000..497459d35ea --- /dev/null +++ b/tests/swoole_iouring/mix.phpt @@ -0,0 +1,194 @@ +--TEST-- +swoole_runtime/file_hook: support io_uring +--SKIPIF-- + +--FILE-- + 32, + 'iouring_entries' => 30000, +]; + +if (defined('SWOOLE_IOURING_SQPOLL')) { + $setting['iouring_flag'] = SWOOLE_IOURING_SQPOLL; +} + +swoole_async_set($setting); + +$results = []; +for ($i = 1; $i <= 10000; $i++) { + $results[$i] = random_bytes(rand(8192, 8192 * 3)); +} + +run(function() use ($results) { + $filesize = 1048576; + $content = random_bytes($filesize); + $fileName = '/tmp/test_file'; + Assert::eq(file_put_contents($fileName, $content), 1048576); + var_dump(stat($fileName)); + for ($i = 0; $i < 100; $i++) { + Assert::eq(filesize($fileName), 1048576); + Assert::eq(file_get_contents($fileName), $content); + } + unlink($fileName); + Assert::true(!file_exists($fileName)); + + $stream = fopen($fileName, 'w'); + fwrite($stream, $content); + if (PHP_VERSION_ID >= 80100) { + Assert::true(fdatasync($stream)); + Assert::true(fsync($stream)); + } + Assert::eq(file_get_contents($fileName), $content); + var_dump(fstat($stream)); + fclose($stream); + unlink($fileName); + + file_put_contents($fileName, $content); + rename($fileName, $fileName.'aaa'); + Assert::true(!file_exists($fileName)); + Assert::true(file_exists($fileName.'aaa')); + unlink($fileName.'aaa'); + + $directory = '/tmp/a/b/c/d/e/f'; + mkdir($directory, 0755, true); + Assert::true(is_dir($directory)); + rmdir($directory); + Assert::true(!is_dir($directory)); + + $waitGroup = new WaitGroup(); + for ($i = 1; $i <= 10000; $i++) { + go(function() use ($waitGroup, $i, $results){ + $waitGroup->add(); + file_put_contents('/tmp/file'.$i, $results[$i]); + Assert::true($results[$i] == file_get_contents('/tmp/file'.$i)); + file_put_contents('/tmp/file'.$i, $results[$i], FILE_APPEND); + file_put_contents('/tmp/file'.$i, $results[$i], FILE_APPEND); + Assert::true(strlen($results[$i]) * 3 == strlen(file_get_contents('/tmp/file'.$i))); + $waitGroup->done(); + }); + } + $waitGroup->wait(); + echo 'SUCCESS'; +}); +?> +--EXPECTF-- +array(26) { + [0]=> + int(%d) + [1]=> + int(%d) + [2]=> + int(%d) + [3]=> + int(%d) + [4]=> + int(%d) + [5]=> + int(%d) + [6]=> + int(%d) + [7]=> + int(%d) + [8]=> + int(%d) + [9]=> + int(%d) + [10]=> + int(%d) + [11]=> + int(%d) + [12]=> + int(%d) + ["dev"]=> + int(%d) + ["ino"]=> + int(%d) + ["mode"]=> + int(%d) + ["nlink"]=> + int(%d) + ["uid"]=> + int(%d) + ["gid"]=> + int(%d) + ["rdev"]=> + int(%d) + ["size"]=> + int(%d) + ["atime"]=> + int(%d) + ["mtime"]=> + int(%d) + ["ctime"]=> + int(%d) + ["blksize"]=> + int(%d) + ["blocks"]=> + int(%d) +} +array(26) { + [0]=> + int(%d) + [1]=> + int(%d) + [2]=> + int(%d) + [3]=> + int(%d) + [4]=> + int(%d) + [5]=> + int(%d) + [6]=> + int(%d) + [7]=> + int(%d) + [8]=> + int(%d) + [9]=> + int(%d) + [10]=> + int(%d) + [11]=> + int(%d) + [12]=> + int(%d) + ["dev"]=> + int(%d) + ["ino"]=> + int(%d) + ["mode"]=> + int(%d) + ["nlink"]=> + int(%d) + ["uid"]=> + int(%d) + ["gid"]=> + int(%d) + ["rdev"]=> + int(%d) + ["size"]=> + int(%d) + ["atime"]=> + int(%d) + ["mtime"]=> + int(%d) + ["ctime"]=> + int(%d) + ["blksize"]=> + int(%d) + ["blocks"]=> + int(%d) +} +SUCCESS diff --git a/tests/swoole_iouring/setting.phpt b/tests/swoole_iouring/setting.phpt new file mode 100644 index 00000000000..ff3dcd290e8 --- /dev/null +++ b/tests/swoole_iouring/setting.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_http_server: iouring setting test +--SKIPIF-- + +--FILE-- +initRandomData(1); +$pm->parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + $headers = httpGetHeaders("http://127.0.0.1:{$pm->getFreePort()}"); + }); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->set([ + 'iouring_flag' => SWOOLE_IOURING_SQPOLL, + 'iouring_entries' => 4096, + 'iouring_workers' => 16 + ]); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + $response->status(200, "status"); + $response->end("Hello World"); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +DONE diff --git a/tests/swoole_library/curl/basic/1.phpt b/tests/swoole_library/curl/basic/1.phpt deleted file mode 100644 index 33e8bc7a1d4..00000000000 --- a/tests/swoole_library/curl/basic/1.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_exec() function with basic functionality ---CREDITS-- -Sebastian Deutsch - -TestFest 2009 - AFUP - Jean-Marc Fontaine - ---SKIPIF-- - ---FILE-- -run(function ($host) { - // start testing - echo "*** Testing curl_exec() : basic functionality ***\n"; - - $url = $host. "/get.php?test=get"; - $ch = curl_init(); - - ob_start(); // start output buffering - curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use - $ok = curl_exec($ch); - curl_close($ch); - $curl_content = ob_get_contents(); - ob_end_clean(); - - if ($ok) { - var_dump($curl_content); - } else { - echo "curl_exec returned false"; - } -}); - -?> ---EXPECTF-- -*** Testing curl_exec() : basic functionality *** -string(25) "Hello World! -Hello World!" diff --git a/tests/swoole_library/curl/basic/10.phpt b/tests/swoole_library/curl/basic/10.phpt deleted file mode 100644 index 9adb34e431a..00000000000 --- a/tests/swoole_library/curl/basic/10.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_error() & curl_errno() function with problematic proxy ---CREDITS-- -TestFest 2009 - AFUP - Perrick Penet ---SKIPIF-- - - ---FILE-- -run(function ($host) { - $url = "http://www.example.org"; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_PROXY, uniqid() . ":" . uniqid()); - curl_setopt($ch, CURLOPT_URL, $url); - - curl_exec($ch); - var_dump(curl_error($ch)); - var_dump(curl_errno($ch)); - curl_close($ch); - -}, false); - -?> ---EXPECTF-- -string(%d) "%r(Couldn't resolve proxy|Could not resolve proxy:|Could not resolve host:|Could not resolve:)%r %s" -int(5) diff --git a/tests/swoole_library/curl/basic/14.phpt b/tests/swoole_library/curl/basic/14.phpt deleted file mode 100644 index cc79ede92ee..00000000000 --- a/tests/swoole_library/curl/basic/14.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_init() function with basic functionality ---CREDITS-- -Jean-Marc Fontaine ---SKIPIF-- - - ---FILE-- -run(function ($host) { - $ch = curl_init(); - Assert::isInstanceOf($ch, Swoole\Curl\Handler::class); - -}, false); - -?> -===DONE=== ---EXPECT-- -===DONE=== diff --git a/tests/swoole_library/curl/basic/15.phpt b/tests/swoole_library/curl/basic/15.phpt deleted file mode 100644 index b95f67fa636..00000000000 --- a/tests/swoole_library/curl/basic/15.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_init() function with $url parameter defined ---CREDITS-- -Jean-Marc Fontaine ---SKIPIF-- - - ---FILE-- -run(function ($host) { - $url = 'http://www.example.com/'; - $ch = curl_init($url); - - Assert::same($url, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); -}, false); - -?> -===DONE=== ---EXPECT-- -===DONE=== diff --git a/tests/swoole_library/curl/basic/19.phpt b/tests/swoole_library/curl/basic/19.phpt deleted file mode 100644 index 050b6a1cf10..00000000000 --- a/tests/swoole_library/curl/basic/19.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_getinfo() function with CURLINFO_EFFECTIVE_URL parameter ---CREDITS-- -Jean-Marc Fontaine ---SKIPIF-- - ---FILE-- -run(function ($host) { - - $url = "http://{$host}/get.php?test="; - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_URL, $url); - curl_exec($ch); - $info = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); - var_dump($url == $info); - curl_close($ch); -}); -?> -===DONE=== ---EXPECTF-- -Hello World! -Hello World!bool(true) -===DONE=== diff --git a/tests/swoole_library/curl/basic/2.phpt b/tests/swoole_library/curl/basic/2.phpt deleted file mode 100644 index ccbab922964..00000000000 --- a/tests/swoole_library/curl/basic/2.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_opt() function with CURLOPT_RETURNTRANSFER parameter set to 1 ---CREDITS-- -Sebastian Deutsch -TestFest 2009 - AFUP - Jean-Marc Fontaine ---SKIPIF-- - ---FILE-- -run(function ($host) { - // start testing - echo '*** Testing curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); ***' . "\n"; - - $url = "{$host}/get.php?test=get"; - $ch = curl_init(); - - ob_start(); // start output buffering - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use - - $curl_content = curl_exec($ch); - curl_close($ch); - - var_dump( $curl_content ); -}); -?> -===DONE=== ---EXPECTF-- -*** Testing curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); *** -string(25) "Hello World! -Hello World!" -===DONE=== diff --git a/tests/swoole_library/curl/basic/20.phpt b/tests/swoole_library/curl/basic/20.phpt deleted file mode 100644 index 34baf375700..00000000000 --- a/tests/swoole_library/curl/basic/20.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_getinfo() function with CURLINFO_HTTP_CODE parameter ---CREDITS-- -Jean-Marc Fontaine ---SKIPIF-- - ---FILE-- -run(function ($host) { - $url = "{$host}/get.php?test="; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_exec($ch); - var_dump(curl_getinfo($ch, CURLINFO_HTTP_CODE)); - curl_close($ch); -}); -?> -===DONE=== ---EXPECTF-- -Hello World! -Hello World!int(200) -===DONE=== diff --git a/tests/swoole_library/curl/basic/21.phpt b/tests/swoole_library/curl/basic/21.phpt deleted file mode 100644 index bdacdf83fa3..00000000000 --- a/tests/swoole_library/curl/basic/21.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_getinfo() function with CURLINFO_CONTENT_TYPE parameter ---CREDITS-- -Jean-Marc Fontaine ---SKIPIF-- - ---FILE-- -run(function ($host) { - - $url = "{$host}/get.php?test=contenttype"; - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_exec($ch); - var_dump(curl_getinfo($ch, CURLINFO_CONTENT_TYPE)); - curl_close($ch); -}); - -?> -===DONE=== ---EXPECTF-- -string(24) "text/plain;charset=utf-8" -===DONE=== diff --git a/tests/swoole_library/curl/basic/3.phpt b/tests/swoole_library/curl/basic/3.phpt deleted file mode 100644 index 465a38a6a93..00000000000 --- a/tests/swoole_library/curl/basic/3.phpt +++ /dev/null @@ -1,50 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_opt() function with POST parameters ---CREDITS-- -Sebastian Deutsch -TestFest 2009 - AFUP - Jean-Marc Fontaine ---SKIPIF-- - ---FILE-- -run(function ($host) { - - // start testing - echo '*** Testing curl sending through GET an POST ***' . "\n"; - - $url = "{$host}/get.php?test=getpost&get_param=Hello%20World"; - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, "Hello=World&Foo=Bar&Person=John%20Doe"); - curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use - - $curl_content = curl_exec($ch); - curl_close($ch); - - var_dump( $curl_content ); -}); -?> -===DONE=== ---EXPECTF-- -*** Testing curl sending through GET an POST *** -string(208) "array(2) { - ["test"]=> - string(7) "getpost" - ["get_param"]=> - string(11) "Hello World" -} -array(3) { - ["Hello"]=> - string(5) "World" - ["Foo"]=> - string(3) "Bar" - ["Person"]=> - string(8) "John Doe" -} -" -===DONE=== diff --git a/tests/swoole_library/curl/basic/4.phpt b/tests/swoole_library/curl/basic/4.phpt deleted file mode 100644 index 9ec9458402a..00000000000 --- a/tests/swoole_library/curl/basic/4.phpt +++ /dev/null @@ -1,43 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_opt() function with setting referer ---CREDITS-- -Sebastian Deutsch -TestFest 2009 - AFUP - Jean-Marc Fontaine ---SKIPIF-- - ---FILE-- -run(function ($host) { - - // start testing - echo '*** Testing curl setting referer ***' . "\n"; - - $url = "{$host}/get.php?test=referer"; - $ch = curl_init(); - - ob_start(); // start output buffering - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_REFERER, 'http://www.refer.er'); - curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use - - $curl_content = curl_exec($ch); - curl_close($ch); - - var_dump($curl_content); -}); - -?> -===DONE=== ---EXPECTF-- -*** Testing curl setting referer *** -string(19) "http://www.refer.er" -===DONE=== diff --git a/tests/swoole_library/curl/basic/5.phpt b/tests/swoole_library/curl/basic/5.phpt deleted file mode 100644 index 49bb88e00b5..00000000000 --- a/tests/swoole_library/curl/basic/5.phpt +++ /dev/null @@ -1,44 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_opt() function with user agent ---CREDITS-- -Sebastian Deutsch -TestFest 2009 - AFUP - Jean-Marc Fontaine ---SKIPIF-- - ---FILE-- -run(function ($host) { - // start testing - echo '*** Testing curl with user agent ***' . "\n"; - - $url = "{$host}/get.php?test=useragent"; - $ch = curl_init(); - - ob_start(); // start output buffering - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_USERAGENT, 'cURL phpt'); - curl_setopt($ch, CURLOPT_URL, $url); //set the url we want to use - - $curl_content = curl_exec($ch); - curl_close($ch); - - var_dump( $curl_content ); - -}); - - -?> -===DONE=== ---EXPECTF-- -*** Testing curl with user agent *** -string(9) "cURL phpt" -===DONE=== diff --git a/tests/swoole_library/curl/basic/7.phpt b/tests/swoole_library/curl/basic/7.phpt deleted file mode 100644 index 43a66b3d8fe..00000000000 --- a/tests/swoole_library/curl/basic/7.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_error() & curl_errno() function without url ---CREDITS-- -TestFest 2009 - AFUP - Perrick Penet ---SKIPIF-- - - ---FILE-- -run(function ($host) { - - $ch = curl_init(); - - curl_exec($ch); - var_dump(curl_error($ch)); - var_dump(curl_errno($ch)); - curl_close($ch); - -}, false); - -?> ---EXPECTF-- -%string(%d) "No URL set or URL using bad/illegal format" -int(3) diff --git a/tests/swoole_library/curl/basic/8.phpt b/tests/swoole_library/curl/basic/8.phpt deleted file mode 100644 index e0ea4c0842c..00000000000 --- a/tests/swoole_library/curl/basic/8.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_error() & curl_errno() function with problematic host ---CREDITS-- -TestFest 2009 - AFUP - Perrick Penet ---SKIPIF-- - ---FILE-- -run(function ($host) { - $url = "http://www." . uniqid() . "." . uniqid(); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_exec($ch); - var_dump(curl_error($ch)); - var_dump(curl_errno($ch)); - curl_close($ch); -}, false); -?> ---EXPECTF-- -%s resolve%s -int(6) diff --git a/tests/swoole_library/curl/basic/9.phpt b/tests/swoole_library/curl/basic/9.phpt deleted file mode 100644 index fbf3d93afb6..00000000000 --- a/tests/swoole_library/curl/basic/9.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -swoole_library/curl/basic: Test curl_error() & curl_errno() function with problematic protocol ---CREDITS-- -TestFest 2009 - AFUP - Perrick Penet ---SKIPIF-- - - ---FILE-- -run(function ($host) { - $url = uniqid()."://www.".uniqid().".".uniqid(); - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - - curl_exec($ch); - var_dump(curl_error($ch)); - var_dump(curl_errno($ch)); - curl_close($ch); - -}, false); -?> ---EXPECTREGEX-- -string\(\d+\) ".+URL.+" -int\(\d\) diff --git a/tests/swoole_library/curl/setopt/1.phpt b/tests/swoole_library/curl/setopt/1.phpt deleted file mode 100644 index b34c5697012..00000000000 --- a/tests/swoole_library/curl/setopt/1.phpt +++ /dev/null @@ -1,50 +0,0 @@ ---TEST-- -swoole_library/curl/setopt: curl_setopt_array() function - tests setting multiple cURL options with curl_setopt_array() ---CREDITS-- -Mattijs Hoitink mattijshoitink@gmail.com -#Testfest Utrecht 2009 ---SKIPIF-- - ---FILE-- -run(function ($host) { - - // Use the set Environment variable - $url = "{$host}/get.php?test=get"; - -// Start the test - echo '== Starting test curl_setopt_array($ch, $options); ==' . "\n"; - -// curl handler - $ch = curl_init(); - -// options for the curl handler - $options = array( - CURLOPT_URL => $url, - CURLOPT_RETURNTRANSFER => 1 - ); - - - curl_setopt_array($ch, $options); - $returnContent = curl_exec($ch); - curl_close($ch); - - var_dump($returnContent); -}); - - -?> ---EXPECT-- -== Starting test curl_setopt_array($ch, $options); == -string(25) "Hello World! -Hello World!" diff --git a/tests/swoole_library/curl/setopt/3.phpt b/tests/swoole_library/curl/setopt/3.phpt deleted file mode 100644 index f1e1f35dc2c..00000000000 --- a/tests/swoole_library/curl/setopt/3.phpt +++ /dev/null @@ -1,49 +0,0 @@ ---TEST-- -swoole_library/curl/setopt: curl_setopt() call with CURLOPT_HTTPHEADER ---CREDITS-- -Paul Sohier -#phptestfest utrecht ---SKIPIF-- - ---FILE-- -run(function ($host) { - // start testing - echo "*** curl_setopt() call with CURLOPT_HTTPHEADER\n"; - - $url = "{$host}/"; - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_HTTPHEADER, 1); - - $curl_content = curl_exec($ch); - curl_close($ch); - - var_dump( $curl_content ); - - $ch = curl_init(); - - ob_start(); // start output buffering - curl_setopt($ch, CURLOPT_HTTPHEADER, array()); - curl_setopt($ch, CURLOPT_URL, $host); - - $curl_content = curl_exec($ch); - ob_end_clean(); - curl_close($ch); - - var_dump( $curl_content ); - -}); - - -?> ---EXPECTF-- -*** curl_setopt() call with CURLOPT_HTTPHEADER - -Warning: swoole_curl_setopt(): You must pass either an object or an array with the CURLOPT_HTTPHEADER argument in %s on line %d -bool(false) -bool(true) diff --git a/tests/swoole_library/curl/setopt/4.phpt b/tests/swoole_library/curl/setopt/4.phpt deleted file mode 100644 index 099d8c2cce2..00000000000 --- a/tests/swoole_library/curl/setopt/4.phpt +++ /dev/null @@ -1,50 +0,0 @@ ---TEST-- -swoole_library/curl/setopt: curl_setopt() call with CURLOPT_RETURNTRANSFER ---CREDITS-- -Paul Sohier -#phptestfest utrecht ---SKIPIF-- - ---FILE-- -run(function ($host) { - -// start testing - echo "*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 1\n"; - - $url = "{$host}/"; - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_URL, $url); - - $curl_content = curl_exec($ch); - curl_close($ch); - - var_dump( $curl_content ); - - echo "*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 0\n"; - - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0); - curl_setopt($ch, CURLOPT_URL, $url); - ob_start(); - $curl_content = curl_exec($ch); - ob_end_clean(); - curl_close($ch); - - var_dump( $curl_content ); - -}); - -?> ---EXPECTF-- -*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 1 -string(%d) "%a" -*** curl_setopt() call with CURLOPT_RETURNTRANSFER set to 0 -bool(true) diff --git a/tests/swoole_library/curl/setopt/filetime_1.phpt b/tests/swoole_library/curl/setopt/filetime_1.phpt deleted file mode 100644 index 746fe439027..00000000000 --- a/tests/swoole_library/curl/setopt/filetime_1.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -swoole_library/curl/setopt: CURLOPT_FILETIME ---SKIPIF-- - ---FILE-- -run(function ($host) { - - $ch = curl_init(); - - $options = array( - CURLOPT_URL => 'http://www.gov.cn/govweb/xhtml/libs/jQuery/jquery-e.min.js', - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_FILETIME => true, - CURLOPT_NOBODY => true, - ); - - curl_setopt_array($ch, $options); - curl_exec($ch); - $info = curl_getinfo($ch); - Assert::assert(!empty($info['filetime'])); - Assert::greaterThanEq($info['filetime'], 1000000); - curl_close($ch); - echo "END\n"; -}, false); - -?> ---EXPECT-- -END diff --git a/tests/swoole_library/curl/ssl/version.phpt b/tests/swoole_library/curl/ssl/version.phpt deleted file mode 100644 index f028f1dd90c..00000000000 --- a/tests/swoole_library/curl/ssl/version.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -swoole_library/curl/ssl: Test SSL_VERSION option ---SKIPIF-- - - ---FILE-- -run(function ($host) { - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); - 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/"); - $result = curl_exec($ch); - Assert::assert($result); - Assert::contains($result, 'tencent'); - curl_close($ch); - -}, false); - -?> ---EXPECTF-- diff --git a/tests/swoole_library/curl/template b/tests/swoole_library/curl/template deleted file mode 100644 index 8c7bef89e96..00000000000 --- a/tests/swoole_library/curl/template +++ /dev/null @@ -1,7 +0,0 @@ -require __DIR__ . '/../../../include/bootstrap.php'; - -$cm = new \SwooleTest\CurlManager(); -$cm->run(function ($host) { - - -}); \ No newline at end of file diff --git a/tests/swoole_library/curl/upload/1.phpt b/tests/swoole_library/curl/upload/1.phpt deleted file mode 100644 index 168f0f3708d..00000000000 --- a/tests/swoole_library/curl/upload/1.phpt +++ /dev/null @@ -1,85 +0,0 @@ ---TEST-- -swoole_library/curl/upload: CURL file uploading ---INI-- ---SKIPIF-- - ---FILE-- -run(function ($host) { - - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, "{$host}/get.php?test=file"); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - - testcurl($ch, __DIR__ . '/curl_testdata1.txt'); - testcurl($ch, __DIR__ . '/curl_testdata1.txt', 'text/plain'); - testcurl($ch, __DIR__ . '/curl_testdata1.txt', '', 'foo.txt'); - testcurl($ch, __DIR__ . '/curl_testdata1.txt', 'text/plain', 'foo.txt'); - - $file = new CurlFile(__DIR__ . '/curl_testdata1.txt'); - $file->setMimeType('text/plain'); - var_dump($file->getMimeType()); - var_dump($file->getFilename()); - curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); - var_dump(curl_exec($ch)); - - $file = curl_file_create(__DIR__ . '/curl_testdata1.txt'); - $file->setPostFilename('foo.txt'); - var_dump($file->getPostFilename()); - curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); - var_dump(curl_exec($ch)); - - curl_setopt($ch, CURLOPT_SAFE_UPLOAD, 0); - $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); - curl_setopt($ch, CURLOPT_POSTFIELDS, $params); - var_dump(curl_exec($ch)); - - curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true); - $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); - curl_setopt($ch, CURLOPT_POSTFIELDS, $params); - var_dump(curl_exec($ch)); - - curl_setopt($ch, CURLOPT_URL, "{$host}/get.php?test=post"); - $params = array('file' => '@' . __DIR__ . '/curl_testdata1.txt'); - curl_setopt($ch, CURLOPT_POSTFIELDS, $params); - var_dump(curl_exec($ch)); - - curl_close($ch); -}); - -function testcurl($ch, $name, $mime = '', $postname = '') -{ - if(!empty($postname)) { - $file = new CurlFile($name, $mime, $postname); - } else if(!empty($mime)) { - $file = new CurlFile($name, $mime); - } else { - $file = new CurlFile($name); - } - curl_setopt($ch, CURLOPT_POSTFIELDS, array("file" => $file)); - $ret = curl_exec($ch) or die("ERROR: Code=".curl_errno($ch). "Msg=".curl_error($ch)."\n"); - var_dump($ret); -} -?> ---EXPECTF-- -string(%d) "curl_testdata1.txt|application/octet-stream" -string(%d) "curl_testdata1.txt|text/plain" -string(%d) "foo.txt|application/octet-stream" -string(%d) "foo.txt|text/plain" -string(%d) "text/plain" -string(%d) "%s/curl_testdata1.txt" -string(%d) "curl_testdata1.txt|text/plain" -string(%d) "foo.txt" -string(%d) "foo.txt|application/octet-stream" - -Warning: swoole_curl_setopt(): Disabling safe uploads is no longer supported in %s on line %d -string(0) "" -string(0) "" -string(%d) "array(1) { - ["file"]=> - string(%d) "@%s/curl_testdata1.txt" -} -" 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/lockwait_twice.phpt b/tests/swoole_lock/lockwait_twice.phpt new file mode 100644 index 00000000000..89655b78cf4 --- /dev/null +++ b/tests/swoole_lock/lockwait_twice.phpt @@ -0,0 +1,23 @@ +--TEST-- +swoole_lock: test lock twice +--SKIPIF-- + +--FILE-- +lock()); + + +$start = microtime(true); +$ret = $lock->lockwait(0.2); +Assert::false($ret); +$end = microtime(true); + +Assert::eq($lock->errCode, SOCKET_ETIMEDOUT); +Assert::lessThan($end - $start, 0.2); + +?> +--EXPECT-- +bool(true) 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 6907b05f137..00000000000 --- a/tests/swoole_mysql_coro/another_coroutine.phpt +++ /dev/null @@ -1,66 +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 -DONE 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_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 new file mode 100644 index 00000000000..74ffbaaffe9 --- /dev/null +++ b/tests/swoole_process/signal_in_manager.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_process: signal in manager +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + usleep(100000); + $manager_pid = file_get_contents(PID_FILE); + Process::kill($manager_pid, SIGINT); + $pm->wait(); + $pm->kill(); +}; + +$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('ManagerStart', function (Server $serv) use ($pm) { + file_put_contents(PID_FILE, $serv->getManagerPid()); + Process::signal(SIGINT, function () use ($pm) { + echo "SIGINT triggered\n"; + $pm->wakeup(); + }); + $pm->wakeup(); + }); + $serv->on('Receive', function (Server $serv, $fd, $reactorId, $data) { + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(PID_FILE); +?> +--EXPECT-- +SIGINT triggered diff --git a/tests/swoole_process/signal_in_task_worker.phpt b/tests/swoole_process/signal_in_task_worker.phpt new file mode 100644 index 00000000000..58972efad7c --- /dev/null +++ b/tests/swoole_process/signal_in_task_worker.phpt @@ -0,0 +1,54 @@ +--TEST-- +swoole_process: signal in task worker +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $_pid = file_get_contents(PID_FILE); + Process::kill($_pid, SIGINT); + $pm->wait(); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set([ + 'worker_num' => 1, + 'task_worker_num' => 1, + 'log_file' => '/dev/null' + ]); + + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + if ($serv->taskworker) { + file_put_contents(PID_FILE, $serv->getWorkerPid()); + Process::signal(SIGINT, function () use($pm) { + echo "SIGINT triggered\n"; + $pm->wakeup(); + }); + $pm->wakeup(); + } + }); + $serv->on("Task", function (Server $serv) use ($pm) { + + }); + $serv->on("Receive", function (Server $serv, $fd, $reactorId, $data) { + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +unlink(PID_FILE); +?> +--EXPECT-- +SIGINT triggered diff --git a/tests/swoole_process/signal_twice.phpt b/tests/swoole_process/signal_twice.phpt new file mode 100644 index 00000000000..2471385c9fb --- /dev/null +++ b/tests/swoole_process/signal_twice.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_process: signal +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $n = N; + while($n--) { + Process::kill($pid, SIGUSR1); + $pm->wait(); + } + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $n = N; + while($n--) { + run(static function () use($n, $pm){ + $running = true; + Process::signal(SIGUSR1, function() use(&$running, $n) { + $running = false; + echo 'sigusr1 one-'.$n.PHP_EOL; + }); + $pm->wakeup(); + go(static function () use(&$running) { + while ($running) { + Co::sleep(0.1); + } + }); + }); + } + $pm->wakeup(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +sigusr1 one-1 +sigusr1 one-0 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 new file mode 100644 index 00000000000..62a13a4495b --- /dev/null +++ b/tests/swoole_process_pool/detach.phpt @@ -0,0 +1,68 @@ +--TEST-- +swoole_process_pool: detach +--SKIPIF-- + +--FILE-- +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}"; + fwrite($fp, pack('N', strlen($msg)) . $msg); + } + $pm->wait(); + Assert::eq($atomic->get(), N + 1); + echo "DONE\n"; + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm, $atomic) { + $pool = new Pool(1, SWOOLE_IPC_SOCKET); + + $pool->on('WorkerStart', function (Pool $pool, $workerId) use ($pm, $atomic) { + echo("[Worker #{$workerId}] WorkerStart\n"); + if ($atomic->get() == 0) { + $pm->wakeup(); + } + }); + + $pool->on('Message', function (Pool $pool, $msg) use ($pm, $atomic) { + if ($atomic->get() == 0) { + $atomic->add(); + $pool->detach(); + $n = N; + while ($n--) { + usleep(1000); + $atomic->add(); + } + $pm->wakeup(); + } else { + echo $msg . PHP_EOL; + } + }); + + $pool->listen('127.0.0.1', $pm->getFreePort()); + $pool->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +[Worker #0] WorkerStart +[Worker #0] WorkerStart +HELLO-2 +DONE diff --git a/tests/swoole_process_pool/getprocess_3.phpt b/tests/swoole_process_pool/getprocess_3.phpt new file mode 100644 index 00000000000..b66cc57694a --- /dev/null +++ b/tests/swoole_process_pool/getprocess_3.phpt @@ -0,0 +1,39 @@ +--TEST-- +swoole_process_pool: getProcess [3] +--SKIPIF-- + +--FILE-- +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(10000); + $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_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/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/curl_native/error.phpt b/tests/swoole_runtime/curl_native/error.phpt deleted file mode 100644 index d8b1bfdc5b0..00000000000 --- a/tests/swoole_runtime/curl_native/error.phpt +++ /dev/null @@ -1,41 +0,0 @@ ---TEST-- -swoole_runtime/curl_native: error ---SKIPIF-- - ---FILE-- - ---EXPECT-- -Done diff --git a/tests/swoole_runtime/curl_native/sleep.phpt b/tests/swoole_runtime/curl_native/sleep.phpt deleted file mode 100644 index 908e30ae427..00000000000 --- a/tests/swoole_runtime/curl_native/sleep.phpt +++ /dev/null @@ -1,69 +0,0 @@ ---TEST-- -swoole_runtime/curl_native: sleep ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - Runtime::enableCoroutine(SWOOLE_HOOK_NATIVE_CURL); - $s = microtime(true); - run(function () use ($pm) { - $n = N; - while($n--) { - go(function() use ($pm) { - $ch = curl_init(); - $code = uniqid('swoole_'); - $url = "http://127.0.0.1:".$pm->getFreePort()."/?code=".urlencode($code); - - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_HEADER, 0); - curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $strHeader) { - return strlen($strHeader); - }); - - $output = curl_exec($ch); - Assert::eq($output, "Hello World\n".$code); - if ($output === false) { - echo "CURL Error:" . curl_error($ch); - } - curl_close($ch); - }); - } - }); - Assert::lessThan(microtime(true) - $s, 0.5); - $pm->kill(); - echo "Done\n"; -}; -$pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server("127.0.0.1", $pm->getFreePort()); - $http->set(['worker_num' => N, 'log_file' => '/dev/null']); - - $http->on("start", function ($server) use ($pm) { - $pm->wakeup(); - }); - - $http->on("request", function (Request $request, Response $response) { - usleep(300000); - $response->end("Hello World\n".$request->get['code']); - }); - $http->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---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/include_2.phpt b/tests/swoole_runtime/file_hook/include_2.phpt new file mode 100644 index 00000000000..dd3f78ee564 --- /dev/null +++ b/tests/swoole_runtime/file_hook/include_2.phpt @@ -0,0 +1,43 @@ +--TEST-- +swoole_runtime/file_hook: include +--SKIPIF-- + +--FILE-- + +--EXPECT-- +0-1 +0-2 +1-1 +1-2 +2-1 +2-2 +3-1 +3-2 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/nonblock.phpt b/tests/swoole_runtime/nonblock.phpt new file mode 100644 index 00000000000..2c4e3de4b02 --- /dev/null +++ b/tests/swoole_runtime/nonblock.phpt @@ -0,0 +1,25 @@ +--TEST-- +swoole_runtime: nonblock +--SKIPIF-- + +--FILE-- + +--EXPECT-- 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/ssl/capture_peer_cert.phpt b/tests/swoole_runtime/ssl/capture_peer_cert.phpt new file mode 100644 index 00000000000..fb7cfb4b7c8 --- /dev/null +++ b/tests/swoole_runtime/ssl/capture_peer_cert.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_runtime/ssl: capture_peer_cert +--SKIPIF-- + +--FILE-- + [ + "capture_peer_cert" => true, + 'capture_peer_cert_chain' => true, + 'verify_peer' => false + ] + ]); + $r = stream_socket_client("ssl://{$domain}:443", $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $g); + if (!$r) { + return false; + } + $cont = stream_context_get_params($r); + if (!$cont) { + return false; + } + return $cont; +} + +Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL); + +Co\run(function () { + $result = capture_peer_cert('www.baidu.com'); + $info1 = openssl_x509_parse($result["options"]["ssl"]["peer_certificate"]); + Assert::isArray($info1); + Assert::contains($info1['name'], 'baidu.com'); +}); +?> +--EXPECT-- diff --git a/tests/swoole_runtime/ssl/client.phpt b/tests/swoole_runtime/ssl/client.phpt new file mode 100644 index 00000000000..23050706d4c --- /dev/null +++ b/tests/swoole_runtime/ssl/client.phpt @@ -0,0 +1,32 @@ +--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/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 new file mode 100644 index 00000000000..c5035337b4d --- /dev/null +++ b/tests/swoole_runtime/ssl/local_cert.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_runtime/ssl: client with local_cert/local_pk +--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(); + + $context = stream_context_create(); + stream_context_set_option($context, 'ssl', 'local_cert', SSL_FILE_DIR . '/client.crt'); + stream_context_set_option($context, 'ssl', 'local_pk', SSL_FILE_DIR . '/client.key'); + + $fp = stream_socket_client("ssl://127.0.0.1:8000", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context); + 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/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 new file mode 100644 index 00000000000..f882e7e2e1d --- /dev/null +++ b/tests/swoole_runtime/ssl/without_key.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_runtime/ssl: client without local_pk +--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(); + + $context = stream_context_create(); + stream_context_set_option($context, 'ssl', 'local_cert', SSL_FILE_DIR . '/client.crt'); + + $fp = stream_socket_client("ssl://127.0.0.1:8000", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context); + 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(); +?> +--EXPECTF-- +Warning: stream_socket_client(): ssl require key file in %s on line %d +OK +OK 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/stdin.phpt b/tests/swoole_runtime/stdin.phpt new file mode 100644 index 00000000000..a14a0565a54 --- /dev/null +++ b/tests/swoole_runtime/stdin.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_runtime: stdin +--SKIPIF-- + +--FILE-- + -1, ]); + +$proc = new Process(function ($p) { + Co\run(function () use($p) { + $p->write('start'.PHP_EOL); + go(function() { + co::sleep(0.05); + echo "sleep\n"; + }); + echo fread(STDIN, 1024); + }); +}, true, SOCK_STREAM); +$proc->start(); + +echo $proc->read(); + +usleep(100000); +$proc->write('hello world'.PHP_EOL); + +echo $proc->read(); +echo $proc->read(); + +Process::wait() +?> +--EXPECT-- +start +sleep +hello world 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 new file mode 100644 index 00000000000..ee6f4359197 --- /dev/null +++ b/tests/swoole_runtime/stream_context_pass_null.phpt @@ -0,0 +1,19 @@ +--TEST-- +swoole_runtime: stream context pass null +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) 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 8b7bfa6ba3d..4d4a5c1fa81 100644 --- a/tests/swoole_runtime/stream_get_meta_data.phpt +++ b/tests/swoole_runtime/stream_get_meta_data.phpt @@ -6,6 +6,9 @@ swoole_runtime: stream_get_meta_data parentFunc = function ($pid) use ($pm) -{ +$pm->parentFunc = function ($pid) use ($pm) { $port = $pm->getFreePort(); test($port); - swoole_runtime::enableCoroutine(); - go(function() use($port) { + Runtime::setHookFlags(SWOOLE_HOOK_ALL); + Co\run(function () use ($port) { test($port); }); - swoole_event_wait(); $pm->kill(); }; -$pm->childFunc = function () use ($pm) -{ - $serv = new \swoole_server(TCP_SERVER_HOST, $pm->getFreePort(), SWOOLE_BASE); - $socket = $serv->getSocket(); +$pm->childFunc = function () use ($pm) { + $serv = new Server(TCP_SERVER_HOST, $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) use ($socket) - { + $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/check_callback.phpt b/tests/swoole_server/check_callback.phpt new file mode 100644 index 00000000000..a8cd701760e --- /dev/null +++ b/tests/swoole_server/check_callback.phpt @@ -0,0 +1,83 @@ +--TEST-- +swoole_server: check callback +--SKIPIF-- + +--FILE-- +start(); + }, true, SOCK_STREAM, false); + $proc->start(); + $result = Process::wait(); + Assert::contains($proc->read(), 'require on'.ucfirst($callback).' callback'); + Assert::eq($result['code'], 255); +} + +test_create_server(Swoole\Server::class, Constant::EVENT_RECEIVE); +test_create_server(Swoole\Http\Server::class, Constant::EVENT_REQUEST); +test_create_server(Swoole\WebSocket\Server::class, Constant::EVENT_MESSAGE); + +$proc = new Process(function () { + $server = new Swoole\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_UDP); + $server->start(); +}, true, SOCK_STREAM, false); +$proc->start(); +$result = Process::wait(); +Assert::contains($proc->read(), 'require onPacket callback'); +Assert::eq($result['code'], 255); + +$proc = new Process(function () { + $server = new Swoole\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_UDP); + $server->on(Constant::EVENT_PACKET, function () {}); + $server->addlistener('127.0.0.1', 0, SWOOLE_SOCK_TCP); + $server->start(); +}, true, SOCK_STREAM, false); +$proc->start(); +$result = Process::wait(); +Assert::contains($proc->read(), 'require onReceive callback'); +Assert::eq($result['code'], 255); + +$proc = new Process(function () { + $server = new Swoole\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_TCP); + $server->on(Constant::EVENT_RECEIVE, function () {}); + $server->addlistener('127.0.0.1', 0, SWOOLE_SOCK_UDP); + $server->start(); +}, true, SOCK_STREAM, false); +$proc->start(); +$result = Process::wait(); +Assert::contains($proc->read(), 'require onPacket callback'); +Assert::eq($result['code'], 255); + +$proc = new Process(function () { + $server = new Swoole\Http\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_TCP); + $server->on(Constant::EVENT_REQUEST, function () {}); + $server->addlistener('127.0.0.1', 0, SWOOLE_SOCK_UDP); + $server->start(); +}, true, SOCK_STREAM, false); +$proc->start(); +$result = Process::wait(); +Assert::contains($proc->read(), 'require onPacket callback'); +Assert::eq($result['code'], 255); + +$proc = new Process(function () { + $server = new Swoole\Http\Server('127.0.0.1', 0, SWOOLE_BASE, SWOOLE_SOCK_TCP); + $server->on(Constant::EVENT_REQUEST, function () {}); + $port = $server->addlistener('127.0.0.1', 0, SWOOLE_SOCK_TCP); + $port->set(['open_http_protocol' => false]); + $server->start(); +}, true, SOCK_STREAM, false); +$proc->start(); +$result = Process::wait(); +Assert::contains($proc->read(), 'require onReceive callback'); +Assert::eq($result['code'], 255); + +?> +--EXPECT-- diff --git a/tests/swoole_server/client_close_in_writable_event.phpt b/tests/swoole_server/client_close_in_writable_event.phpt new file mode 100644 index 00000000000..3d5b1016c93 --- /dev/null +++ b/tests/swoole_server/client_close_in_writable_event.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_server: client close in writable event +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) 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("begin"); + $pm->wait(); + usleep(100000); + $client->close(); + $pm->wait(); + $pm->kill(); +}; + +$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', + 'kernel_socket_send_buffer_size' => 128 * 1024, + 'buffer_output_size' => 4 * 1024 * 1024, + ]); + $serv->on(Constant::EVENT_WORKER_START, function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on(Constant::EVENT_CONNECT, function (Server $serv, $fd, $reactor_id) { + echo "CONNECT $fd\n"; + }); + $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) use ($pm) { + $n = 8; + $serv->pause($fd); + while ($n--) { + $serv->send($fd, str_repeat('A', 2 * 1024 * 1024) . "\r\n"); + } + $pm->wakeup(); + }); + $serv->on(Constant::EVENT_CLOSE, function (Server $serv, $fd, $reactor_id) use ($pm) { + echo "CLOSE $fd\n"; + $pm->wakeup(); + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +echo 'DONE'.PHP_EOL; +?> +--EXPECT-- +CONNECT 1 +CLOSE 1 +DONE 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 new file mode 100644 index 00000000000..eb8dda26947 --- /dev/null +++ b/tests/swoole_server/close_max_fd.phpt @@ -0,0 +1,86 @@ +--TEST-- +swoole_server: close_max_fd +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(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('test')); + $client->recv(); + Co::sleep(2); + $client->send('ping'); + Co::sleep(2); + $pm->kill(); + }); + go(function() use ($pm) { + $cli = new Co\Http\Client('127.0.0.1', $pm->getFreePort()); + }); + go(function() use ($pm) { + $client = new Co\Client(SWOOLE_SOCK_TCP); + Assert::assert($client->connect('127.0.0.1', $pm->getFreePort())); + $client->send('test2'); + }); + }); +}; +$pm->childFunc = function () use ($pm) { + $server = new Swoole\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $server->set([ + 'worker_num' => 1, + 'log_level' => SWOOLE_LOG_ERROR, + ]); + + $server->on('receive', function (Swoole\Server $serv, int $fd, int $rid, string $data) { + var_dump($data); + var_dump($serv->getClientList()); + Assert::true(!empty($serv->getClientList())); + foreach ($serv->connections as $_fd) { + var_dump("fd:{$_fd}"); + } + $serv->send($fd, "Server: " . $data); + }); + + $server->on('Close', function ($server, $fd) { + echo "Client:{$fd} Close.\n"; + }); + + $server->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +string(4) "test" +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} +string(4) "fd:1" +string(4) "fd:2" +string(5) "test2" +array(2) { + [0]=> + int(1) + [1]=> + int(2) +} +string(4) "fd:1" +string(4) "fd:2" +Client:2 Close. +string(4) "ping" +array(1) { + [0]=> + int(1) +} +string(4) "fd:1" +Client:1 Close. 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/discard_timeout_packet.phpt b/tests/swoole_server/discard_timeout_packet.phpt index ddd2277e879..07285db43e5 100644 --- a/tests/swoole_server/discard_timeout_packet.phpt +++ b/tests/swoole_server/discard_timeout_packet.phpt @@ -32,7 +32,7 @@ $pm->parentFunc = function () use ($pm) { }); $pm->kill(); Assert::eq(substr_count(file_get_contents(TMP_LOG_FILE), - 'Worker_discard_data (ERRNO 1007): [2] ignore data'), 8); + 'Worker_discard_data() (ERRNO 1007): [2] ignore data'), 8); unlink(TMP_LOG_FILE); }; 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 a73b89425d4..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"); @@ -44,14 +44,14 @@ $pm->parentFunc = function ($pid) use ($pm) { }; $pm->childFunc = function () use ($pm) { - $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); $serv->set(array( '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 new file mode 100644 index 00000000000..07bc726699b --- /dev/null +++ b/tests/swoole_server/dispatch_mode_8.phpt @@ -0,0 +1,95 @@ +--TEST-- +swoole_server: dispatch_mode = 8 [co conn lb] +--SKIPIF-- + +--FILE-- +column('count', Table::TYPE_INT); +$table->create(); + +$pm = new SwooleTest\ProcessManager; +$pm->magic_code = rand(10000000, 90000000); +$pm->parentFunc = function ($pid) use ($pm, $table) { + run(function () use ($pm, $table) { + $n = 200; + while ($n--) { + go(function () use ($pm, $table) { + $client = new Client(SWOOLE_SOCK_TCP); + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + + $data = array( + 'name' => __FILE__, + 'sid' => $pm->magic_code, + 'content' => str_repeat('A', 8192 * rand(1, 3)), + ); + + $_serialize_data = serialize($data) . "\r\n\r\n"; + $client->send($_serialize_data); + Assert::eq($client->recv(), "SUCCESS\n"); + }); + } + }); + + $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( + 'package_eof' => "\r\n\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + 'dispatch_mode' => SWOOLE_DISPATCH_CO_CONN_LB, + 'package_max_length' => 1024 * 1024 * 2, + "worker_num" => 4, + 'log_file' => '/dev/null', + "reload_async" => true, + )); + $serv->on("WorkerStart", function (Server $serv, $worker_id) use ($pm) { + if ($worker_id == 0) { + $pm->wakeup(); + } + }); + $serv->on('connect', function (Server $serv, $fd, $rid) use ($table) { + $table->incr($serv->getWorkerId(), 'count'); + if (rand(1000, 9999) % 4 == 0) { + System::sleep(0.5); + } + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm) { + Assert::eq($serv->getClientInfo($fd)['worker_id'], $serv->getWorkerId()); + $_data = unserialize(rtrim($data)); + if ($_data and is_array($_data) and $_data['sid'] == $pm->magic_code) { + $serv->send($fd, "SUCCESS\n"); + } else { + $serv->send($fd, "ERROR\n"); + } + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/dispatch_mode_9.phpt b/tests/swoole_server/dispatch_mode_9.phpt new file mode 100644 index 00000000000..1db219de004 --- /dev/null +++ b/tests/swoole_server/dispatch_mode_9.phpt @@ -0,0 +1,115 @@ +--TEST-- +swoole_server: dispatch_mode = 9 [co req 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) { + $client = new Client(SWOOLE_SOCK_TCP); + $client->set(array( + 'package_eof' => EOF, + 'open_eof_check' => true, + 'open_eof_split' => true, + )); + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 0.5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + + $rand = rand(1, 4); + + $data = array( + 'name' => __FILE__, + 'sid' => $pm->magic_code, + 'content' => str_repeat('A', 1024 * $rand), + ); + + $_serialize_data = serialize($data) . EOF; + + go(function () use ($client) { + $n = N; + while ($n--) { + Assert::eq($client->recv(), "SUCCESS" . EOF); + } + $client->close(); + }); + + $n = N; + while ($n--) { + $client->send($_serialize_data); + if ($n % 10 == 1) { + 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( + 'package_eof' => "\r\n\r\n", + 'open_eof_check' => true, + 'open_eof_split' => true, + 'dispatch_mode' => SWOOLE_DISPATCH_CO_REQ_LB, + 'package_max_length' => 1024 * 1024 * 2, + "worker_num" => 4, + 'log_file' => '/dev/null', + "reload_async" => true, + )); + + $serv->on("WorkerStart", function (Server $serv, $worker_id) use ($pm) { + if ($worker_id == 0) { + $pm->wakeup(); + } + }); + + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($pm, $table) { + $table->incr($serv->getWorkerId(), 'count'); + if (rand(1000, 9999) % 10 == 0) { + System::sleep(0.5); + } + $_data = unserialize(rtrim($data)); + if ($_data and is_array($_data) and $_data['sid'] == $pm->magic_code) { + $serv->send($fd, "SUCCESS".EOF); + } else { + $serv->send($fd, "ERROR".EOF); + } + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 5335c9c6845..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 kill_timeout_process (ERRNO 9012): [Manager] Worker#%d[pid=%d] exit timeout, force kill the process -[%s] WARNING kill_timeout_process (ERRNO 9012): [Manager] Worker#%d[pid=%d] exit timeout, force kill the process -[%s] WARNING kill_timeout_process (ERRNO 9012): [Manager] Worker#%d[pid=%d] exit timeout, force kill the process -[%s] WARNING kill_timeout_process (ERRNO 9012): [Manager] Worker#%d[pid=%d] exit timeout, force kill the process -[%s] WARNING check_worker_exit_status: worker#%d[pid=%d] abnormal exit, status=0, signal=9 -[%s] WARNING check_worker_exit_status: worker#%d[pid=%d] abnormal exit, status=0, signal=9 -[%s] WARNING check_worker_exit_status: worker#%d[pid=%d] abnormal exit, status=0, signal=9 -[%s] WARNING check_worker_exit_status: worker#%d[pid=%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 8f19c5d9202..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: swKill(%d, SIGKILL) [%d] -[%s] WARNING ProcessPool_kill_timeout_worker: swKill(%d, SIGKILL) [%d] -[%s] WARNING wait: worker#%d abnormal exit, status=0, signal=9 -[%s] WARNING 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/http_protocol.phpt b/tests/swoole_server/http_protocol.phpt new file mode 100644 index 00000000000..30d3155f35d --- /dev/null +++ b/tests/swoole_server/http_protocol.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_server: http request & response +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $httpClient = new Client(HTTP_SERVER_HOST, $pm->getFreePort(), false); + $httpClient->setMethod("POST"); + $httpClient->setData("HELLO"); + $ok = $httpClient->execute("/rawcookie?hello=world&value=1"); + Assert::assert($ok); + Assert::same($httpClient->statusCode, 200); + Assert::same($httpClient->errCode, 0); + Assert::eq($httpClient->getHeaders()['x-server'], 'swoole'); + Assert::same($httpClient->getBody(), GREETER); + echo "DONE\n"; + }); + $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' => '/dev/null', + 'open_http_protocol' => true, + ]); + $serv->on("Start", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $reactor_id, $data) { + $req = Request::create(); + Assert::eq($req->parse($data), strlen($data)); + + $resp = Response::create([$serv, $req], $fd); + $resp->header('X-Server', 'swoole'); + $resp->end(GREETER); + Assert::eq($resp->fd, $fd); + Assert::eq($req->fd, $fd); + }); + $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/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 f0fc1051ed8..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 send_to_connection (ERRNO 1005): send %d byte failed, session#100 does not exist -[%s] NOTICE 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 c4439981321..1a27495093f 100644 --- a/tests/swoole_server/length/03.phpt +++ b/tests/swoole_server/length/03.phpt @@ -9,22 +9,22 @@ require __DIR__ . '/../../include/bootstrap.php'; $port = get_one_free_port(); $size = 8 * 1024 * 1024; +$_g_data = random_bytes($size); use Swoole\Server; $pm = new SwooleTest\ProcessManager; -$pm->parentFunc = function ($pid) use ($port, $pm, $size) { +$pm->parentFunc = function ($pid) use ($port, $pm, $size, $_g_data) { $cli = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC); $cli->connect(TCP_SERVER_HOST, $port, 1); - $data = str_repeat('A', $size); - $cli->send(pack('N', strlen($data)) . $data); + $cli->send(pack('N', strlen($_g_data)) . $_g_data); $recv_data = $cli->recv(); echo $recv_data; $pm->kill(); }; -$pm->childFunc = function () use ($pm, $port, $size) { - $serv = new Server(TCP_SERVER_HOST, $port); +$pm->childFunc = function () use ($pm, $port, $size, $_g_data) { + $serv = new Server(TCP_SERVER_HOST, $port, SWOOLE_PROCESS); $serv->set([ "worker_num" => 1, 'log_file' => '/dev/null', @@ -37,8 +37,9 @@ $pm->childFunc = function () use ($pm, $port, $size) { $serv->on("WorkerStart", function (Server $serv) use ($pm) { $pm->wakeup(); }); - $serv->on("receive", function ($serv, $fd, $rid, $data) use ($size) { - Assert::assert(strlen($data) == $size + 4); + $serv->on("receive", function ($serv, $fd, $rid, $data) use ($size, $_g_data) { + Assert::eq(strlen($data), $size + 4); + Assert::eq($_g_data, substr($data, 4)); $serv->send($fd, "OK\n"); }); $serv->start(); 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 e72f6583cc3..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,15 +83,14 @@ $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} thrown in %s on line %d DONE - 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/single_thread/large_packet.phpt b/tests/swoole_server/single_thread/large_packet.phpt new file mode 100644 index 00000000000..4d793180b77 --- /dev/null +++ b/tests/swoole_server/single_thread/large_packet.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_server/length: package_length_func +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $url = 'http://127.0.0.1:' . $pm->getFreePort() . '/'; + $filePath = tempnam('/tmp', 'swoole_test_'); + $rdata = random_bytes(1024 * 1024); + file_put_contents($filePath, $rdata); + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Accept: text/html', + 'Content-Type: multipart/form-data' + ]); + curl_setopt($ch, CURLOPT_POSTFIELDS, [ + 'file' => new CURLFile($filePath, 'text/html') + ]); + $response = curl_exec($ch); + if (curl_errno($ch)) { + echo 'ERROR: ' . curl_error($ch); + } else { + Assert::eq($response, md5($rdata)); + } + curl_close($ch); + unlink($filePath); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('0.0.0.0', $pm->getFreePort(), SWOOLE_PROCESS); + $http->set([ + 'single_thread' => true, + 'worker_num' => 1, + 'dispatch_mode' => 10, + 'package_max_length' => '128m', + ]); + $http->on('WorkerStart', function (Swoole\Http\Server $serv) use ($pm) { + $pm->wakeup(); + }); + $http->on('Request', function ($request, $response) { + $response->end(md5_file($request->files['file']['tmp_name'])); + }); + $http->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/sleep.phpt b/tests/swoole_server/sleep.phpt index a5218e3ce83..dc6b5aa3c52 100644 --- a/tests/swoole_server/sleep.phpt +++ b/tests/swoole_server/sleep.phpt @@ -1,5 +1,5 @@ --TEST-- -swoole_runtime/curl_native: sleep +swoole_server: sleep --SKIPIF-- 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 new file mode 100644 index 00000000000..f7dca07e8ad --- /dev/null +++ b/tests/swoole_server/slow_master.phpt @@ -0,0 +1,85 @@ +--TEST-- +swoole_server: slow master +--SKIPIF-- + +--FILE-- +add(strlen($data)); +} + +$pm = new SwooleTest\ProcessManager; + +$pm->parentFunc = function ($pid) use ($pm, $counter_server, $counter_client, $data_chunks) { + $cli = new Client(SWOOLE_SOCK_TCP); + $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 5); + Assert::assert($r); + $cli->send('hello world'); + + $n = $counter_client->get(); + $data = ''; + while (strlen($data) < $n) { + $_recv = $cli->recv(); + if (empty($_recv)) { + break; + } + $data .= $_recv; + } + Assert::eq($data, implode('', $data_chunks)); + $cli->close(); + $pm->kill(); + Assert::greaterThanEq($counter_server->get(), 5); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm, $counter_server, $counter_client, $data_chunks) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS); + $serv->set(array( + 'worker_num' => 1, + 'log_file' => '/dev/null', + 'single_thread' => true, + )); + + $serv->on(Constant::EVENT_START, function () use ($pm) { + Timer::after(5, function (){ + usleep(300000); + }); + $pm->wakeup(); + }); + + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($counter_server, $counter_client, $data_chunks) { + $serv->timer = Timer::tick(50, function () use ($counter_server) { + $counter_server->add(1); + }); + foreach ($data_chunks as $chunk) { + $serv->send($fd, $chunk); + } + }); + + $serv->on(Constant::EVENT_CLOSE, function ($serv) use ($pm) { + Timer::clear($serv->timer); + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE diff --git a/tests/swoole_server/slow_worker.phpt b/tests/swoole_server/slow_worker.phpt new file mode 100644 index 00000000000..3c0dab84314 --- /dev/null +++ b/tests/swoole_server/slow_worker.phpt @@ -0,0 +1,80 @@ +--TEST-- +swoole_server: slow worker +--SKIPIF-- + +--FILE-- +add(strlen($data)); +} + +$pm = new SwooleTest\ProcessManager; +$pm->parentFunc = function ($pid) use ($pm, $counter_server, $counter_client, $data_chunks) { + $cli = new Client(SWOOLE_SOCK_TCP); + $r = $cli->connect(TCP_SERVER_HOST, $pm->getFreePort(), 5); + Assert::assert($r); + Assert::eq($cli->recv(), 'CONNECT OK'.PHP_EOL); + + foreach ($data_chunks as $chunk) { + $cli->send($chunk); + } + + $cli2 = new Client(SWOOLE_SOCK_TCP); + $r = $cli2->connect(TCP_SERVER_HOST, $pm->getFreePort(), 5); + Assert::assert($r); + Assert::eq($cli2->recv(), 'CONNECT OK'.PHP_EOL); + + echo $cli->recv(); + Assert::eq($counter_server->get(), $counter_client->get()); + $pm->kill(); +}; + +$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, + 'log_file' => '/dev/null', + 'open_length_check' => true, + 'package_max_length' => 16 * 1024 * 1024, + 'package_length_type' => 'N', + 'package_length_offset' => 0, + 'package_body_offset' => 4, + )); + + $serv->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + usleep(100000); + }); + + $serv->on(Swoole\Constant::EVENT_CONNECT, function (Server $serv, $fd, $rid) { + $serv->send($fd, 'CONNECT OK'.PHP_EOL); + }); + + $serv->on('receive', function (Server $serv, $fd, $rid, $data) use ($counter_server, $counter_client) { + $counter_server->add(strlen($data)); + if ($counter_server->get() == $counter_client->get()) { + $serv->send($fd, "DONE\n"); + } + }); + + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 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 2667d819233..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, @@ -45,4 +45,4 @@ readfile(ERROR_FILE); unlink(ERROR_FILE); ?> --EXPECTF-- -[%s] WARNING ssl_accept: bad SSL client[127.0.0.1:%d], reason=%d, error_string=%s +[%s] WARNING Socket::ssl_accept(): bad SSL client[127.0.0.1:%d], reason=%d, error_string=%s 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 new file mode 100644 index 00000000000..182c12c97a9 --- /dev/null +++ b/tests/swoole_server/ssl/heartbeat_1.phpt @@ -0,0 +1,48 @@ +--TEST-- +swoole_server/ssl: heartbeat with bad client +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new 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"); + } + $s1 = time(); + Assert::same($client->recv(), ''); + $s2 = time(); + Assert::assert($s2 - $s1 > 1); + Swoole\Process::kill($pid); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv->set(array( + 'heartbeat_check_interval' => 1, + 'log_file' => '/dev/null', + 'heartbeat_idle_time' => 1, + 'ssl_cert_file' => __DIR__ . '/../../include/api/ssl-ca/server-cert.pem', + 'ssl_key_file' => __DIR__ . '/../../include/api/ssl-ca/server-key.pem', + )); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- diff --git a/tests/swoole_server/ssl/heartbeat_2.phpt b/tests/swoole_server/ssl/heartbeat_2.phpt new file mode 100644 index 00000000000..42e2cf202e8 --- /dev/null +++ b/tests/swoole_server/ssl/heartbeat_2.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_server/ssl: heartbeat normal +--SKIPIF-- + +--FILE-- +parentFunc = function ($pid) use ($pm) { + $client = new Client(SWOOLE_SOCK_TCP | SWOOLE_SSL, SWOOLE_SOCK_SYNC); + if (!$client->connect('127.0.0.1', $pm->getFreePort(), 5, 0)) { + echo "Over flow. errno=" . $client->errCode; + die("\n"); + } + $s1 = microtime(true); + Assert::same($client->recv(), ''); + $s2 = microtime(true); + Assert::assert($s2 - $s1 > 1); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) { + $serv = new Server('127.0.0.1', $pm->getFreePort(), SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL); + $serv->set(array( + 'heartbeat_check_interval' => 1, + 'log_file' => '/dev/null', + 'heartbeat_idle_time' => 1, + 'ssl_cert_file' => __DIR__ . '/../../include/api/ssl-ca/server-cert.pem', + 'ssl_key_file' => __DIR__ . '/../../include/api/ssl-ca/server-key.pem', + )); + $serv->on("workerStart", function ($serv) use ($pm) { + $pm->wakeup(); + }); + $serv->on('receive', function (Server $serv, $fd, $rid, $data) { + }); + $serv->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- 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 92e0e9dafbd..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', @@ -47,4 +47,4 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -[%s] NOTICE ssl_verify (ERRNO %d): can not verify peer from fd#%d with error#%d: certificate has expired +[%s] NOTICE Socket::ssl_verify() (ERRNO %d): can not verify peer from fd#%d with error#%d: certificate has expired 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 e08db352972..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( [ @@ -63,5 +64,5 @@ $pm->run(); unlink(TMP_LOG_FILE); ?> --EXPECTF-- -[%s] WARNING send_blocking(:%d): send %d bytes failed, Error: Resource temporarily unavailable[11] -[%s] WARNING reply_task_result: TaskWorker: send result to worker timed out +[%s] WARNING Socket::send_blocking(): send %d bytes failed, Error: Resource temporarily unavailable[11] +[%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/scheduler_warning.phpt b/tests/swoole_server/task/scheduler_warning.phpt index 4a8dbc9952d..9ddbf8564d0 100644 --- a/tests/swoole_server/task/scheduler_warning.phpt +++ b/tests/swoole_server/task/scheduler_warning.phpt @@ -72,4 +72,4 @@ unlink(LOG_FILE); ?> --EXPECTF-- DONE -[%s] WARNING timer_callback (ERRNO %d): No idle task worker is available +[%s] WARNING Server::timer_callback() (ERRNO %d): No idle task worker is available 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/unregistered_signal.phpt b/tests/swoole_server/unregistered_signal.phpt index 8199bac5511..8abab97ec24 100644 --- a/tests/swoole_server/unregistered_signal.phpt +++ b/tests/swoole_server/unregistered_signal.phpt @@ -37,4 +37,4 @@ $pm->childFirst(); $pm->run(); ?> --EXPECTF-- -[%s] WARNING %s (ERRNO 707): Unable to find callback function for signal Broken pipe: 13 +[%s] WARNING %s (ERRNO 721): Unable to find callback function for signal Broken pipe: 13 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.phpt b/tests/swoole_server_port/heartbeat.phpt deleted file mode 100644 index 80d69c69f87..00000000000 --- a/tests/swoole_server_port/heartbeat.phpt +++ /dev/null @@ -1,87 +0,0 @@ ---TEST-- -swoole_server_port: heartbeat ---SKIPIF-- - ---FILE-- -initFreePorts(2); - -$pm->parentFunc = function ($pid) use ($pm) -{ - go(function () use ($pm) - { - $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); - $cli->connect('127.0.0.1', $pm->getFreePort(0)); - for ($i = 0; $i < 2; ++$i) { - $cli->send('hello'); - $data = $cli->recv(); - if ($data != 'ok') { - echo "ERROR\n"; - } - co::sleep(2); - } - co::sleep(3); - $cli->send('hello'); - $data = $cli->recv(); - if ($data == 'ok') { - echo "ERROR\n"; - } - }); - - go(function () use ($pm) - { - $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); - $cli->connect('127.0.0.1', $pm->getFreePort(1)); - for ($i = 0; $i < 2; ++$i) { - $cli->send('hello'); - $data = $cli->recv(); - if ($data != 'ok') { - echo "ERROR\n"; - } - co::sleep(2); - } - co::sleep(3); - $cli->send('hello'); - $data = $cli->recv(); - if ($data == 'ok') { - echo "ERROR\n"; - } else { - echo "OK"; - } - }); - - 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->set([ - 'heartbeat_check_interval' => 1, - 'heartbeat_idle_time' => 3 - ]); - $server->on('receive', function ($server, $fd, $reactorId, $data) { - $server->send($fd, 'ok'); - }); - - $port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); - // $port2->set([ - // 'heartbeat_check_interval' => 1, - // 'heartbeat_idle_time' => 5 - // ]); - $port2->on('receive', function ($server, $fd, $reactorId, $data) { - $server->send($fd, 'ok'); - }); - - $server->start(); -}; - -$pm->childFirst(); -$pm->run(); -?> ---EXPECT-- -OK diff --git a/tests/swoole_server_port/heartbeat_1.phpt b/tests/swoole_server_port/heartbeat_1.phpt new file mode 100644 index 00000000000..301809d339b --- /dev/null +++ b/tests/swoole_server_port/heartbeat_1.phpt @@ -0,0 +1,93 @@ +--TEST-- +swoole_server_port: heartbeat 1 +--SKIPIF-- + +--FILE-- +initFreePorts(2); + +$pm->parentFunc = function ($pid) use ($pm) +{ + run(function () use ($pm) { + go(function () use ($pm) { + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $cli->connect('127.0.0.1', $pm->getFreePort(0)); + for ($i = 0; $i < 2; ++$i) { + $cli->send('hello'); + $data = $cli->recv(); + if ($data != 'ok') { + echo "ERROR\n"; + } + System::sleep(2); + } + System::sleep(3); + $cli->send('hello'); + $data = $cli->recv(); + if ($data == 'ok') { + echo "ERROR\n"; + } + }); + + go(function () use ($pm) { + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $cli->connect('127.0.0.1', $pm->getFreePort(1)); + for ($i = 0; $i < 2; ++$i) { + $cli->send('hello'); + $data = $cli->recv(); + if ($data != 'ok') { + echo "ERROR\n"; + } + System::sleep(2); + } + System::sleep(3); + $cli->send('hello'); + $data = $cli->recv(); + if ($data == 'ok') { + echo "ERROR\n"; + } else { + echo "OK"; + } + }); + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) +{ + $server = new Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $server->set([ + 'heartbeat_check_interval' => 1, + 'heartbeat_idle_time' => 3 + ]); + $server->on('receive', function (Server $server, $fd, $reactorId, $data) { + $server->send($fd, 'ok'); + }); + $server->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + + $port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); + // $port2->set([ + // 'heartbeat_check_interval' => 1, + // 'heartbeat_idle_time' => 5 + // ]); + $port2->on('receive', function (Server $server, $fd, $reactorId, $data) { + $server->send($fd, 'ok'); + }); + + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +OK diff --git a/tests/swoole_server_port/heartbeat_2.phpt b/tests/swoole_server_port/heartbeat_2.phpt new file mode 100644 index 00000000000..2e74ef628ef --- /dev/null +++ b/tests/swoole_server_port/heartbeat_2.phpt @@ -0,0 +1,74 @@ +--TEST-- +swoole_server_port: heartbeat 2 +--SKIPIF-- + +--FILE-- +initFreePorts(3); + +$pm->parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $test_func = function ($port_index, $sleep_seconds) use ($pm) { + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $cli->connect('127.0.0.1', $pm->getFreePort($port_index)); + System::sleep($sleep_seconds); + return $cli->recv(0.01); + }; + go(function () use ($test_func) { + Assert::same($test_func(0, 1.3), ''); + echo "DONE 0\n"; + }); + go(function () use ($test_func) { + Assert::same($test_func(1, 2.3), ''); + echo "DONE 1\n"; + }); + go(function () use ($test_func) { + Assert::same($test_func(2, 3.3), false); + echo "DONE 2\n"; + }); + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) +{ + $server = new Server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE); + $server->set([ + 'heartbeat_check_interval' => 1, + 'heartbeat_idle_time' => 1, + ]); + $server->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + $server->on('receive', function ($server, $fd, $reactorId, $data) { + $server->send($fd, 'ok'); + }); + + $port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); + $port2->set([ + 'heartbeat_idle_time' => 2, + ]); + + $port3 = $server->listen('127.0.0.1', $pm->getFreePort(2), SWOOLE_SOCK_TCP); + $port3->set([ + 'heartbeat_idle_time' => 10, + ]); + + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 0 +DONE 1 +DONE 2 diff --git a/tests/swoole_server_port/heartbeat_3.phpt b/tests/swoole_server_port/heartbeat_3.phpt new file mode 100644 index 00000000000..0225856b8ce --- /dev/null +++ b/tests/swoole_server_port/heartbeat_3.phpt @@ -0,0 +1,60 @@ +--TEST-- +swoole_server_port: heartbeat 3 +--SKIPIF-- + +--FILE-- +initFreePorts(3); + +$pm->parentFunc = function ($pid) use ($pm) { + run(function () use ($pm) { + $test_func = function ($port_index, $sleep_seconds) use ($pm) { + $cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP); + $cli->connect('127.0.0.1', $pm->getFreePort($port_index)); + System::sleep($sleep_seconds); + return $cli->recv(0.01); + }; + go(function () use ($test_func) { + Assert::same($test_func(0, 3), false); + echo "DONE 0\n"; + }); + go(function () use ($test_func) { + Assert::same($test_func(1, 2.3), ''); + echo "DONE 1\n"; + }); + }); + $pm->kill(); +}; + +$pm->childFunc = function () use ($pm) +{ + $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'); + }); + $server->on("WorkerStart", function (Server $serv) use ($pm) { + $pm->wakeup(); + }); + + $port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); + $port2->set([ + 'heartbeat_idle_time' => 2, + ]); + + $server->start(); +}; + +$pm->childFirst(); +$pm->run(); +?> +--EXPECT-- +DONE 1 +DONE 0 diff --git a/tests/swoole_server_port/http.phpt b/tests/swoole_server_port/http.phpt index cfb87408b23..ccbca8aeb7e 100644 --- a/tests/swoole_server_port/http.phpt +++ b/tests/swoole_server_port/http.phpt @@ -20,7 +20,7 @@ $pm->parentFunc = function ($pid) use ($pm) if (!$cli->connect('127.0.0.1', $pm->getFreePort(0), 0.5)) { fail: - echo "ERROR\n"; + echo "ERROR 1\n"; return; } //no eof, should be timeout here @@ -36,24 +36,23 @@ $pm->parentFunc = function ($pid) use ($pm) echo "OK\n"; }); - go(function () use ($pm) - { + go(function () use ($pm) { $cli = new Swoole\Coroutine\Http\Client('127.0.0.1', $pm->getFreePort(1)); - if ( $cli->get("/") ) { + if ($cli->get("/")) { echo $cli->body; Assert::same($cli->statusCode, 200); } else { - echo "ERROR\n"; + echo "ERROR 2\n"; } }); - 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, @@ -68,21 +67,18 @@ $pm->childFunc = function () use ($pm) $port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP); $port2->set(['open_http_protocol' => true,]); - $port2->on("request", function ($req, $resp) - { + $port2->on("request", function ($req, $resp) { $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/recv_line.phpt b/tests/swoole_socket_coro/recv_line.phpt new file mode 100644 index 00000000000..c9795e8ed2f --- /dev/null +++ b/tests/swoole_socket_coro/recv_line.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_socket_coro: recv line +--SKIPIF-- + +--FILE-- +handle(function (Connection $conn) use ($server) { + $conn->send("hello world\n"); + $conn->send("\r"); + $conn->send(str_repeat('A', 128) . str_repeat('B', 125)); + $server->shutdown(); + }); + + $server->start(); + }); + + $cli = new Co\Socket(AF_INET, SOCK_STREAM, 0); + if ($cli->connect('127.0.0.1', $port) == false) { + echo "ERROR\n"; + return; + } + Assert::eq($cli->recvLine(128), "hello world\n"); + Assert::eq($cli->recvLine(128), "\r"); + Assert::eq($cli->recvLine(128), str_repeat('A', 128)); + Assert::eq($cli->recvLine(128), str_repeat('B', 125)); + Assert::eq($cli->recvLine(128), ""); +}); + +?> +--EXPECT-- diff --git a/tests/swoole_socket_coro/recv_with_buffer.phpt b/tests/swoole_socket_coro/recv_with_buffer.phpt new file mode 100644 index 00000000000..539b8f2426d --- /dev/null +++ b/tests/swoole_socket_coro/recv_with_buffer.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_socket_coro: recv with buffer +--SKIPIF-- + +--FILE-- +handle(function (Connection $conn) use ($server) { + $conn->send(DATA); + $server->shutdown(); + }); + + $server->start(); + }); + + $cli = new Co\Socket(AF_INET, SOCK_STREAM, 0); + if ($cli->connect('127.0.0.1', $port) == false) { + echo "ERROR\n"; + return; + } + + $data = ''; + while (1) { + $char = $cli->recvWithBuffer(1); + if (strlen($char) == 1) { + $data .= $char; + } else { + break; + } + } + Assert::eq($data, DATA); +}); +?> +--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 new file mode 100644 index 00000000000..b6cbc730511 --- /dev/null +++ b/tests/swoole_socket_coro/ssl.phpt @@ -0,0 +1,20 @@ +--TEST-- +swoole_socket_coro: ssl client +--SKIPIF-- + +--FILE-- + +--EXPECT-- diff --git a/tests/swoole_socket_coro/ssl_bad_server.phpt b/tests/swoole_socket_coro/ssl_bad_server.phpt new file mode 100644 index 00000000000..f690f34fa15 --- /dev/null +++ b/tests/swoole_socket_coro/ssl_bad_server.phpt @@ -0,0 +1,24 @@ +--TEST-- +swoole_socket_coro: ssl connect with bad server +--SKIPIF-- + +--FILE-- + SWOOLE_LOG_WARNING]); + +run(function () { + $cli = new Swoole\Coroutine\Socket(AF_INET, SOCK_STREAM, 0); + $cli->setProtocol(['open_ssl' => true,]); + + Assert::false ($cli->connect('www.baidu.com', 80)); + Assert::eq($cli->errCode, SWOOLE_ERROR_SSL_HANDSHAKE_FAILED); +}); +?> +--EXPECT-- 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/create_10k_object.phpt b/tests/swoole_table/create_10k_object.phpt new file mode 100644 index 00000000000..95486ac2159 --- /dev/null +++ b/tests/swoole_table/create_10k_object.phpt @@ -0,0 +1,14 @@ +--TEST-- +swoole_table: create 10,000 objects +--SKIPIF-- + +--FILE-- + +--EXPECT-- +DONE diff --git a/tests/swoole_table/del.phpt b/tests/swoole_table/del.phpt new file mode 100644 index 00000000000..70efaff8f6e --- /dev/null +++ b/tests/swoole_table/del.phpt @@ -0,0 +1,69 @@ +--TEST-- +swoole_table: clear all columns +--SKIPIF-- + +--FILE-- +column('state', Swoole\Table::TYPE_INT); +$table->column('remainLen', Swoole\Table::TYPE_INT); +$table->column('data', Swoole\Table::TYPE_STRING, 64); +$table->create(); + +function data($table) { + $table_key = 'table'; + $table->incr($table_key, 'state'); + $state = $table->get($table_key, 'state'); + var_dump($state); + $data = $table->get($table_key, 'data'); + $data .= 'abc'; + $table->set($table_key, ['data' => $data]); + var_dump($table->get($table_key)); + + if ($state === 1) { + $table->incr($table_key, 'remainLen', 3); + } else { + $remainLen = $table->get($table_key, 'remainLen'); + if ($remainLen === 3) { + $res = $table->del($table_key); + var_dump($res); + var_dump($table->get($table_key)); + } + } +} +data($table); +data($table); +data($table); +?> +--EXPECT-- +int(1) +array(3) { + ["state"]=> + int(1) + ["remainLen"]=> + int(0) + ["data"]=> + string(3) "abc" +} +int(2) +array(3) { + ["state"]=> + int(2) + ["remainLen"]=> + int(3) + ["data"]=> + string(6) "abcabc" +} +bool(true) +bool(false) +int(1) +array(3) { + ["state"]=> + int(1) + ["remainLen"]=> + int(0) + ["data"]=> + string(3) "abc" +} diff --git a/tests/swoole_table/force_unlock.phpt b/tests/swoole_table/force_unlock.phpt new file mode 100644 index 00000000000..894da422193 --- /dev/null +++ b/tests/swoole_table/force_unlock.phpt @@ -0,0 +1,47 @@ +--TEST-- +swoole_table: force unlock +--SKIPIF-- + +--FILE-- +column('string', \Swoole\Table::TYPE_STRING, 4 * 1024 * 1024); +$table->column('int', \Swoole\Table::TYPE_INT, 8); +$table->create(); +$str_size = 4 * 1024 * 1024; +$str_value = random_bytes($str_size); +$data = [ + 'string' => $str_value, + 'int' => PHP_INT_MAX +]; +$table->set('test', $data); + +$proc = new Process(function () use ($table) { + $str = str_repeat('A', 5 * 1024 * 1024); + // Fatal error: memory exhausted + $data = $table->get('test'); + var_dump(strlen($data['string'])); + var_dump(strlen($str)); + var_dump(memory_get_usage()); +}, true, SOCK_STREAM); + +$proc->start(); + +$exit_status = Process::wait(); +Assert::eq($exit_status['code'], 255); +Assert::contains($proc->read(), 'Fatal error: Allowed memory'); + +$data = $table->get('test'); +Assert::eq(strlen($data['string']), $str_size); +Assert::eq($data['string'], $str_value); +echo "Done\n"; +?> +--EXPECTF-- +[%s] WARNING TableRow::lock(): lock process[%d] not exists, force unlock +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 7f576414f30..126a226c479 100644 --- a/tests/swoole_table/key_value.phpt +++ b/tests/swoole_table/key_value.phpt @@ -6,17 +6,20 @@ swoole_table: key-value operate column('id', swoole_table::TYPE_INT); -$table->column('name', swoole_table::TYPE_STRING, 128); -$table->column('num', swoole_table::TYPE_FLOAT); +$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); if (!$table->create()) { echo __LINE__." error"; } -if (!$table->set('test_key', array('id' => 1, 'name' => 'rango', 'num' => 3.1415926))) +if (!$table->set('test_key', array('id' => 1, 'name' => NAME, 'num' =>PI))) { echo __LINE__." error"; } @@ -33,6 +36,13 @@ if (!($ret and $ret == 1)) echo __LINE__." error"; } +Assert::eq($table->get('test_key', 'name'), NAME); +Assert::eq($table->get('test_key', 'num'), PI); + +// field not exists +Assert::false($table->get('test_key', 'id_no_exists')); +Assert::false($table->get('test_key_no_exists', 'id_no_exists')); + $ret = $table->exist('test_key'); if (!($ret)) { @@ -82,7 +92,7 @@ if ($table->exist('test_key')) { echo __LINE__." error"; } -echo "SUCCESS"; +echo "SUCCESS\n"; ?> --EXPECT-- SUCCESS 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/row.phpt b/tests/swoole_table/row.phpt deleted file mode 100644 index 40311f0c5a0..00000000000 --- a/tests/swoole_table/row.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -swoole_table: row ---SKIPIF-- - ---FILE-- -column('bar', swoole_table::TYPE_STRING, 255); -$table->create(); -$table->set('rpw', ['bar' => 'hello world']); -$row = $table['rpw']; -@$row['not_exist'] = '666'; -Assert::true(!isset($row['not_exist'])); -echo $row['bar'] . "\n"; -$row['bar'] = 'hello swoole'; -echo $row['bar'] . "\n"; -$row['bar'] = null; -echo $row['bar'] . "EOF\n"; -Assert::true(isset($row['bar'])); -?> ---EXPECT-- -hello world -hello swoole -EOF 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 new file mode 100644 index 00000000000..8630460d876 --- /dev/null +++ b/tests/swoole_websocket_server/onDisconnct.phpt @@ -0,0 +1,67 @@ +--TEST-- +swoole_websocket_server: onDisconnect +--SKIPIF-- + +--FILE-- +parentFunc = function (int $pid) use ($pm) { + run(function () use ($pm) { + $data = httpGetBody('http://127.0.0.1:' . $pm->getFreePort() . '/'); + Assert::contains($data, 'HTTP 400 Bad Request'); + + $client = new Client('127.0.0.1', $pm->getFreePort()); + Assert::assert($client->upgrade('/websocket')); + Assert::eq($client->getStatusCode(), 101); + $client->push('hello world'); + $client->close(); + }); + $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' => '/dev/null' + ]); + $serv->on('WorkerStart', function () use ($pm) { + $pm->wakeup(); + }); + $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); + }); + $serv->on('disconnect', function ($s, $id) use ($pm) { + puts("disconnect " . $id); + }); + $serv->on('open', function ($s, $req) use ($pm) { + puts("open " . $req->fd); + }); + $serv->on('close', function ($s, $id) use ($pm) { + puts("close " . $id); + }); + $serv->start(); +}; +$pm->childFirst(); +$pm->run(); +puts('done!'); +?> +--EXPECT-- +connect 1 +disconnect 1 +connect 2 +open 2 +close 2 +done! 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 cbe77512e37..3aeb528fd97 100644 --- a/thirdparty/boost/asm/combined.S +++ b/thirdparty/boost/asm/combined.S @@ -1,22 +1,16 @@ -#ifdef __linux__ - #if defined(__i386__) - #include "make_i386_sysv_elf_gas.S" - #include "jump_i386_sysv_elf_gas.S" - #elif defined(__x86_64__) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) + #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 @@ -25,4 +19,4 @@ #include "jump_combined_sysv_macho_gas.S" #else #error "not supports" -#endif \ No newline at end of file +#endif diff --git a/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S b/thirdparty/boost/asm/jump_arm64_aapcs_elf_gas.S index 09bd7b52259..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,6 +109,6 @@ jump_fcontext: add sp, sp, #0xb0 ret x4 -.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_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 3019aeb49dc..00000000000 --- a/thirdparty/boost/asm/jump_arm_aapcs_elf_gas.S +++ /dev/null @@ -1,93 +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 - -@ Mark that we don't need executable stack. -.section .note.GNU-stack,"",%progbits 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 1d27afad100..773e8345dfe 100644 --- a/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S +++ b/thirdparty/boost/asm/jump_combined_sysv_macho_gas.S @@ -7,14 +7,12 @@ // 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(__arm64__) + #include "jump_arm64_aapcs_macho_gas.S" #else #error "No arch's" #endif 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 d5380d50f23..00000000000 --- a/thirdparty/boost/asm/jump_ppc32_sysv_elf_gas.S +++ /dev/null @@ -1,208 +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 - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits 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 46bc3cc2c81..a90ffbe1681 100644 --- a/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S +++ b/thirdparty/boost/asm/jump_ppc64_sysv_elf_gas.S @@ -12,253 +12,207 @@ * ------------------------------------------------- * * | 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 + std %r0, 176(%r1) - 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 - 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 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 ac555319e93..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,87 +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 */ + +#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 */ - /* restore return-address */ - popq %r8 + leaq 0x48(%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 */ +#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 /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits 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 b208ab7f39d..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,6 +80,6 @@ finish: # exit application bl _exit -.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_arm64_aapcs_macho_gas.S b/thirdparty/boost/asm/make_arm64_aapcs_macho_gas.S index 6b55a08adfc..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,28 +51,22 @@ * * *******************************************************/ - .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' - - ; TODO: Numeric offset since llvm still does not support labels in ADR. Fix: - ; http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20140407/212336.html - adr x1, 0x0c + adr x1, finish ; save address of finish as return-address for context-function ; will be entered after context-function returns (LR register) 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 9877655161b..00000000000 --- a/thirdparty/boost/asm/make_arm_aapcs_elf_gas.S +++ /dev/null @@ -1,71 +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 - -@ Mark that we don't need executable stack. -.section .note.GNU-stack,"",%progbits 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 727e9045fc1..226dd9a16d5 100644 --- a/thirdparty/boost/asm/make_combined_sysv_macho_gas.S +++ b/thirdparty/boost/asm/make_combined_sysv_macho_gas.S @@ -7,14 +7,12 @@ // 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(__arm64__) + #include "make_arm64_aapcs_macho_gas.S" #else #error "No arch's" #endif 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 30ce473c9eb..00000000000 --- a/thirdparty/boost/asm/make_ppc32_sysv_elf_gas.S +++ /dev/null @@ -1,123 +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 - -/* Mark that we don't need executable stack. */ -.section .note.GNU-stack,"",%progbits 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 b777fe69b9e..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,12 +164,12 @@ 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 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 b6878795846..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,64 +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 /* Mark that we don't need executable stack. */ .section .note.GNU-stack,"",%progbits 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/alloc.c b/thirdparty/hiredis/alloc.c new file mode 100644 index 00000000000..7fb6b35e778 --- /dev/null +++ b/thirdparty/hiredis/alloc.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2020, Michael Grunder + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "fmacros.h" +#include "alloc.h" +#include +#include + +hiredisAllocFuncs hiredisAllocFns = { + .mallocFn = malloc, + .callocFn = calloc, + .reallocFn = realloc, + .strdupFn = strdup, + .freeFn = free, +}; + +/* Override hiredis' allocators with ones supplied by the user */ +hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *override) { + hiredisAllocFuncs orig = hiredisAllocFns; + + hiredisAllocFns = *override; + + return orig; +} + +/* Reset allocators to use libc defaults */ +void hiredisResetAllocators(void) { + hiredisAllocFns = (hiredisAllocFuncs) { + .mallocFn = malloc, + .callocFn = calloc, + .reallocFn = realloc, + .strdupFn = strdup, + .freeFn = free, + }; +} + +#ifdef _WIN32 + +void *hi_malloc(size_t size) { + return hiredisAllocFns.mallocFn(size); +} + +void *hi_calloc(size_t nmemb, size_t size) { + return hiredisAllocFns.callocFn(nmemb, size); +} + +void *hi_realloc(void *ptr, size_t size) { + return hiredisAllocFns.reallocFn(ptr, size); +} + +char *hi_strdup(const char *str) { + return hiredisAllocFns.strdupFn(str); +} + +void hi_free(void *ptr) { + hiredisAllocFns.freeFn(ptr); +} + +#endif diff --git a/thirdparty/hiredis/alloc.h b/thirdparty/hiredis/alloc.h new file mode 100644 index 00000000000..34a05f49f31 --- /dev/null +++ b/thirdparty/hiredis/alloc.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2020, Michael Grunder + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Redis nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HIREDIS_ALLOC_H +#define HIREDIS_ALLOC_H + +#include /* for size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure pointing to our actually configured allocators */ +typedef struct hiredisAllocFuncs { + void *(*mallocFn)(size_t); + void *(*callocFn)(size_t,size_t); + void *(*reallocFn)(void*,size_t); + char *(*strdupFn)(const char*); + void (*freeFn)(void*); +} hiredisAllocFuncs; + +hiredisAllocFuncs hiredisSetAllocators(hiredisAllocFuncs *ha); +void hiredisResetAllocators(void); + +#ifndef _WIN32 + +/* Hiredis' configured allocator function pointer struct */ +extern hiredisAllocFuncs hiredisAllocFns; + +static inline void *hi_malloc(size_t size) { + return hiredisAllocFns.mallocFn(size); +} + +static inline void *hi_calloc(size_t nmemb, size_t size) { + return hiredisAllocFns.callocFn(nmemb, size); +} + +static inline void *hi_realloc(void *ptr, size_t size) { + return hiredisAllocFns.reallocFn(ptr, size); +} + +static inline char *hi_strdup(const char *str) { + return hiredisAllocFns.strdupFn(str); +} + +static inline void hi_free(void *ptr) { + hiredisAllocFns.freeFn(ptr); +} + +#else + +void *hi_malloc(size_t size); +void *hi_calloc(size_t nmemb, size_t size); +void *hi_realloc(void *ptr, size_t size); +char *hi_strdup(const char *str); +void hi_free(void *ptr); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* HIREDIS_ALLOC_H */ diff --git a/thirdparty/hiredis/hiredis.c b/thirdparty/hiredis/hiredis.c index 69265eb9bbe..41298addcd0 100644 --- a/thirdparty/hiredis/hiredis.c +++ b/thirdparty/hiredis/hiredis.c @@ -34,7 +34,6 @@ #include "fmacros.h" #include #include -#include #include #include #include @@ -43,13 +42,25 @@ #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); +extern int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout); + +static redisContextFuncs redisContextDefaultFuncs = { + .free_privctx = NULL, + .read_ = redisNetRead, + .write_ = redisNetWrite +}; + static redisReply *createReplyObject(int type); static void *createStringObject(const redisReadTask *task, char *str, size_t len); -static void *createArrayObject(const redisReadTask *task, int elements); +static void *createArrayObject(const redisReadTask *task, size_t elements); static void *createIntegerObject(const redisReadTask *task, long long value); +static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len); static void *createNilObject(const redisReadTask *task); +static void *createBoolObject(const redisReadTask *task, int bval); /* Default set of functions to build the reply. Keep in mind that such a * function returning NULL is interpreted as OOM. */ @@ -57,13 +68,15 @@ static redisReplyObjectFunctions defaultFunctions = { createStringObject, createArrayObject, createIntegerObject, + createDoubleObject, createNilObject, + createBoolObject, freeReplyObject }; /* Create a reply object */ static redisReply *createReplyObject(int type) { - redisReply *r = calloc(1,sizeof(*r)); + redisReply *r = hi_calloc(1,sizeof(*r)); if (r == NULL) return NULL; @@ -82,21 +95,29 @@ void freeReplyObject(void *reply) { switch(r->type) { case REDIS_REPLY_INTEGER: + case REDIS_REPLY_NIL: + case REDIS_REPLY_BOOL: break; /* Nothing to free */ case REDIS_REPLY_ARRAY: + case REDIS_REPLY_MAP: + case REDIS_REPLY_SET: + case REDIS_REPLY_PUSH: if (r->element != NULL) { for (j = 0; j < r->elements; j++) freeReplyObject(r->element[j]); - free(r->element); + hi_free(r->element); } break; case REDIS_REPLY_ERROR: case REDIS_REPLY_STATUS: case REDIS_REPLY_STRING: - free(r->str); + case REDIS_REPLY_DOUBLE: + case REDIS_REPLY_VERB: + case REDIS_REPLY_BIGNUM: + hi_free(r->str); break; } - free(r); + hi_free(r); } static void *createStringObject(const redisReadTask *task, char *str, size_t len) { @@ -107,39 +128,56 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len if (r == NULL) return NULL; - buf = malloc(len+1); - if (buf == NULL) { - freeReplyObject(r); - return NULL; - } - assert(task->type == REDIS_REPLY_ERROR || task->type == REDIS_REPLY_STATUS || - task->type == REDIS_REPLY_STRING); + task->type == REDIS_REPLY_STRING || + task->type == REDIS_REPLY_VERB || + task->type == REDIS_REPLY_BIGNUM); /* Copy string value */ - memcpy(buf,str,len); - buf[len] = '\0'; + if (task->type == REDIS_REPLY_VERB) { + buf = hi_malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */ + if (buf == NULL) goto oom; + + memcpy(r->vtype,str,3); + r->vtype[3] = '\0'; + memcpy(buf,str+4,len-4); + buf[len-4] = '\0'; + r->len = len - 4; + } else { + buf = hi_malloc(len+1); + if (buf == NULL) goto oom; + + memcpy(buf,str,len); + buf[len] = '\0'; + r->len = len; + } r->str = buf; - r->len = len; if (task->parent) { parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; } return r; + +oom: + freeReplyObject(r); + return NULL; } -static void *createArrayObject(const redisReadTask *task, int elements) { +static void *createArrayObject(const redisReadTask *task, size_t elements) { redisReply *r, *parent; - r = createReplyObject(REDIS_REPLY_ARRAY); + r = createReplyObject(task->type); if (r == NULL) return NULL; if (elements > 0) { - r->element = calloc(elements,sizeof(redisReply*)); + r->element = hi_calloc(elements,sizeof(redisReply*)); if (r->element == NULL) { freeReplyObject(r); return NULL; @@ -150,7 +188,10 @@ static void *createArrayObject(const redisReadTask *task, int elements) { if (task->parent) { parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; } return r; @@ -167,7 +208,44 @@ static void *createIntegerObject(const redisReadTask *task, long long value) { if (task->parent) { parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); + parent->element[task->idx] = r; + } + return r; +} + +static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_DOUBLE); + if (r == NULL) + return NULL; + + r->dval = value; + r->str = hi_malloc(len+1); + if (r->str == NULL) { + freeReplyObject(r); + return NULL; + } + + /* The double reply also has the original protocol string representing a + * double as a null terminated string. This way the caller does not need + * to format back for string conversion, especially since Redis does efforts + * to make the string more human readable avoiding the calssical double + * decimal string conversion artifacts. */ + memcpy(r->str, str, len); + r->str[len] = '\0'; + r->len = len; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; } return r; @@ -182,7 +260,30 @@ static void *createNilObject(const redisReadTask *task) { if (task->parent) { parent = task->parent->obj; - assert(parent->type == REDIS_REPLY_ARRAY); + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); + parent->element[task->idx] = r; + } + return r; +} + +static void *createBoolObject(const redisReadTask *task, int bval) { + redisReply *r, *parent; + + r = createReplyObject(REDIS_REPLY_BOOL); + if (r == NULL) + return NULL; + + r->integer = bval != 0; + + if (task->parent) { + parent = task->parent->obj; + assert(parent->type == REDIS_REPLY_ARRAY || + parent->type == REDIS_REPLY_MAP || + parent->type == REDIS_REPLY_SET || + parent->type == REDIS_REPLY_PUSH); parent->element[task->idx] = r; } return r; @@ -200,6 +301,7 @@ static uint32_t countDigits(uint64_t v) { v /= 10000U; result += 4; } + return result; } /* Helper that calculates the bulk length given a certain string length. */ @@ -232,7 +334,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { if (*c != '%' || c[1] == '\0') { if (*c == ' ') { if (touched) { - newargv = realloc(curargv,sizeof(char*)*(argc+1)); + newargv = hi_realloc(curargv,sizeof(char*)*(argc+1)); if (newargv == NULL) goto memory_err; curargv = newargv; curargv[argc++] = curarg; @@ -381,7 +483,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { /* Add the last argument if needed */ if (touched) { - newargv = realloc(curargv,sizeof(char*)*(argc+1)); + newargv = hi_realloc(curargv,sizeof(char*)*(argc+1)); if (newargv == NULL) goto memory_err; curargv = newargv; curargv[argc++] = curarg; @@ -397,7 +499,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { totlen += 1+countDigits(argc)+2; /* Build the command at protocol level */ - cmd = malloc(totlen+1); + cmd = hi_malloc(totlen+1); if (cmd == NULL) goto memory_err; pos = sprintf(cmd,"*%d\r\n",argc); @@ -412,7 +514,7 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { assert(pos == totlen); cmd[pos] = '\0'; - free(curargv); + hi_free(curargv); *target = cmd; return totlen; @@ -428,11 +530,11 @@ int redisvFormatCommand(char **target, const char *format, va_list ap) { if (curargv) { while(argc--) sdsfree(curargv[argc]); - free(curargv); + hi_free(curargv); } sdsfree(curarg); - free(cmd); + hi_free(cmd); return error_type; } @@ -473,7 +575,7 @@ int redisFormatCommand(char **target, const char *format, ...) { int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv, const size_t *argvlen) { - sds cmd; + sds cmd, aux; unsigned long long totlen; int j; size_t len; @@ -495,9 +597,13 @@ int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv, return -1; /* We already know how much storage we need */ - cmd = sdsMakeRoomFor(cmd, totlen); - if (cmd == NULL) + aux = sdsMakeRoomFor(cmd, totlen); + if (aux == NULL) { + sdsfree(cmd); return -1; + } + + cmd = aux; /* Construct command */ cmd = sdscatfmt(cmd, "*%i\r\n", argc); @@ -541,7 +647,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz } /* Build the command at protocol level */ - cmd = malloc(totlen+1); + cmd = hi_malloc(totlen+1); if (cmd == NULL) return -1; @@ -562,7 +668,7 @@ int redisFormatCommandArgv(char **target, int argc, const char **argv, const siz } void redisFreeCommand(char *cmd) { - free(cmd); + hi_free(cmd); } void __redisSetError(redisContext *c, int type, const char *str) { @@ -585,15 +691,23 @@ redisReader *redisReaderCreate(void) { return redisReaderCreateWithFunctions(&defaultFunctions); } +static void redisPushAutoFree(void *privdata, void *reply) { + (void)privdata; + freeReplyObject(reply); +} + static redisContext *redisContextInit(void) { redisContext *c; - c = calloc(1,sizeof(redisContext)); + c = hi_calloc(1, sizeof(*c)); if (c == NULL) return NULL; + c->funcs = &redisContextDefaultFuncs; + c->obuf = sdsempty(); c->reader = redisReaderCreate(); + c->fd = REDIS_INVALID_FD; if (c->obuf == NULL || c->reader == NULL) { redisFree(c); @@ -606,22 +720,30 @@ static redisContext *redisContextInit(void) { void redisFree(redisContext *c) { if (c == NULL) return; - if (c->fd > 0) - close(c->fd); + redisNetClose(c); sdsfree(c->obuf); redisReaderFree(c->reader); - free(c->tcp.host); - free(c->tcp.source_addr); - free(c->unix_sock.path); - free(c->timeout); - free(c->saddr); - free(c); + hi_free(c->tcp.host); + hi_free(c->tcp.source_addr); + hi_free(c->unix_sock.path); + hi_free(c->connect_timeout); + hi_free(c->command_timeout); + hi_free(c->saddr); + + if (c->privdata && c->free_privdata) + c->free_privdata(c->privdata); + + if (c->funcs->free_privctx) + c->funcs->free_privctx(c->privctx); + + memset(c, 0xff, sizeof(*c)); + hi_free(c); } -int redisFreeKeepFd(redisContext *c) { - int fd = c->fd; - c->fd = -1; +redisFD redisFreeKeepFd(redisContext *c) { + redisFD fd = c->fd; + c->fd = REDIS_INVALID_FD; redisFree(c); return fd; } @@ -630,136 +752,163 @@ int redisReconnect(redisContext *c) { c->err = 0; memset(c->errstr, '\0', strlen(c->errstr)); - if (c->fd > 0) { - close(c->fd); + if (c->privctx && c->funcs->free_privctx) { + c->funcs->free_privctx(c->privctx); + c->privctx = NULL; } + redisNetClose(c); + sdsfree(c->obuf); redisReaderFree(c->reader); c->obuf = sdsempty(); c->reader = redisReaderCreate(); + if (c->obuf == NULL || c->reader == NULL) { + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; + } + + int ret = REDIS_ERR; if (c->connection_type == REDIS_CONN_TCP) { - return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, - c->timeout, c->tcp.source_addr); + ret = redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, + c->connect_timeout, c->tcp.source_addr); } else if (c->connection_type == REDIS_CONN_UNIX) { - return redisContextConnectUnix(c, c->unix_sock.path, c->timeout); + ret = redisContextConnectUnix(c, c->unix_sock.path, c->connect_timeout); } else { /* Something bad happened here and shouldn't have. There isn't enough information in the context to reconnect. */ __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect"); + ret = REDIS_ERR; } - return REDIS_ERR; -} + if (c->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) { + redisContextSetTimeout(c, *c->command_timeout); + } -/* Connect to a Redis instance. On error the field error in the returned - * context will be set to the return value of the error function. - * When no set of reply functions is given, the default set will be used. */ -redisContext *redisConnect(const char *ip, int port) { - redisContext *c; + return ret; +} - c = redisContextInit(); - if (c == NULL) +redisContext *redisConnectWithOptions(const redisOptions *options) { + redisContext *c = redisContextInit(); + if (c == NULL) { return NULL; + } + if (!(options->options & REDIS_OPT_NONBLOCK)) { + c->flags |= REDIS_BLOCK; + } + if (options->options & REDIS_OPT_REUSEADDR) { + c->flags |= REDIS_REUSEADDR; + } + if (options->options & REDIS_OPT_NOAUTOFREE) { + c->flags |= REDIS_NO_AUTO_FREE; + } - c->flags |= REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,NULL); - return c; -} + /* Set any user supplied RESP3 PUSH handler or use freeReplyObject + * as a default unless specifically flagged that we don't want one. */ + if (options->push_cb != NULL) + redisSetPushCallback(c, options->push_cb); + else if (!(options->options & REDIS_OPT_NO_PUSH_AUTOFREE)) + redisSetPushCallback(c, redisPushAutoFree); -redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { - redisContext *c; + c->privdata = options->privdata; + c->free_privdata = options->free_privdata; - c = redisContextInit(); - if (c == NULL) + if (redisContextUpdateConnectTimeout(c, options->connect_timeout) != REDIS_OK || + redisContextUpdateCommandTimeout(c, options->command_timeout) != REDIS_OK) { + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return c; + } + + if (options->type == REDIS_CONN_TCP) { + redisContextConnectBindTcp(c, options->endpoint.tcp.ip, + options->endpoint.tcp.port, options->connect_timeout, + options->endpoint.tcp.source_addr); + } else if (options->type == REDIS_CONN_UNIX) { + redisContextConnectUnix(c, options->endpoint.unix_socket, + options->connect_timeout); + } else if (options->type == REDIS_CONN_USERFD) { + c->fd = options->endpoint.fd; + c->flags |= REDIS_CONNECTED; + } else { + // Unknown type - FIXME - FREE return NULL; + } + + if (options->command_timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) { + redisContextSetTimeout(c, *options->command_timeout); + } - c->flags |= REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,&tv); return c; } -redisContext *redisConnectNonBlock(const char *ip, int port) { - redisContext *c; +/* Connect to a Redis instance. On error the field error in the returned + * context will be set to the return value of the error function. + * When no set of reply functions is given, the default set will be used. */ +redisContext *redisConnect(const char *ip, int port) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + return redisConnectWithOptions(&options); +} - c = redisContextInit(); - if (c == NULL) - return NULL; +redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.connect_timeout = &tv; + return redisConnectWithOptions(&options); +} - c->flags &= ~REDIS_BLOCK; - redisContextConnectTcp(c,ip,port,NULL); - return c; +redisContext *redisConnectNonBlock(const char *ip, int port) { + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.options |= REDIS_OPT_NONBLOCK; + return redisConnectWithOptions(&options); } redisContext *redisConnectBindNonBlock(const char *ip, int port, const char *source_addr) { - redisContext *c = redisContextInit(); - if (c == NULL) - return NULL; - c->flags &= ~REDIS_BLOCK; - redisContextConnectBindTcp(c,ip,port,NULL,source_addr); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.endpoint.tcp.source_addr = source_addr; + options.options |= REDIS_OPT_NONBLOCK; + return redisConnectWithOptions(&options); } redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, const char *source_addr) { - redisContext *c = redisContextInit(); - if (c == NULL) - return NULL; - c->flags &= ~REDIS_BLOCK; - c->flags |= REDIS_REUSEADDR; - redisContextConnectBindTcp(c,ip,port,NULL,source_addr); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_TCP(&options, ip, port); + options.endpoint.tcp.source_addr = source_addr; + options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR; + return redisConnectWithOptions(&options); } redisContext *redisConnectUnix(const char *path) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectUnix(c,path,NULL); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + return redisConnectWithOptions(&options); } redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags |= REDIS_BLOCK; - redisContextConnectUnix(c,path,&tv); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + options.connect_timeout = &tv; + return redisConnectWithOptions(&options); } redisContext *redisConnectUnixNonBlock(const char *path) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->flags &= ~REDIS_BLOCK; - redisContextConnectUnix(c,path,NULL); - return c; + redisOptions options = {0}; + REDIS_OPTIONS_SET_UNIX(&options, path); + options.options |= REDIS_OPT_NONBLOCK; + return redisConnectWithOptions(&options); } -redisContext *redisConnectFd(int fd) { - redisContext *c; - - c = redisContextInit(); - if (c == NULL) - return NULL; - - c->fd = fd; - c->flags |= REDIS_BLOCK | REDIS_CONNECTED; - return c; +redisContext *redisConnectFd(redisFD fd) { + redisOptions options = {0}; + options.type = REDIS_CONN_USERFD; + options.endpoint.fd = fd; + return redisConnectWithOptions(&options); } /* Set read/write timeout on a blocking socket. */ @@ -776,6 +925,13 @@ int redisEnableKeepAlive(redisContext *c) { return REDIS_OK; } +/* Set a user provided RESP3 PUSH handler and return any old one set. */ +redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn) { + redisPushFn *old = c->push_cb; + c->push_cb = fn; + return old; +} + /* Use this function to handle a read event on the descriptor. It will try * and read some bytes from the socket and feed them to the reply parser. * @@ -789,22 +945,13 @@ int redisBufferRead(redisContext *c) { if (c->err) return REDIS_ERR; - nread = read(c->fd,buf,sizeof(buf)); - if (nread == -1) { - if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - } else { - __redisSetError(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } - } else if (nread == 0) { - __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); + nread = c->funcs->read_(c, buf, sizeof(buf)); + if (nread < 0) { + return REDIS_ERR; + } + if (nread > 0 && redisReaderFeed(c->reader, buf, nread) != REDIS_OK) { + __redisSetError(c, c->reader->err, c->reader->errstr); return REDIS_ERR; - } else { - if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { - __redisSetError(c,c->reader->err,c->reader->errstr); - return REDIS_ERR; - } } return REDIS_OK; } @@ -819,41 +966,64 @@ int redisBufferRead(redisContext *c) { * c->errstr to hold the appropriate error string. */ int redisBufferWrite(redisContext *c, int *done) { - int nwritten; /* Return early when the context has seen an error. */ if (c->err) return REDIS_ERR; if (sdslen(c->obuf) > 0) { - nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); - if (nwritten == -1) { - if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { - /* Try again later */ - } else { - __redisSetError(c,REDIS_ERR_IO,NULL); - return REDIS_ERR; - } + ssize_t nwritten = c->funcs->write_(c); + if (nwritten < 0) { + return REDIS_ERR; } else if (nwritten > 0) { - if (nwritten == (signed)sdslen(c->obuf)) { + if (nwritten == (ssize_t)sdslen(c->obuf)) { sdsfree(c->obuf); c->obuf = sdsempty(); + if (c->obuf == NULL) + goto oom; } else { - sdsrange(c->obuf,nwritten,-1); + if (sdsrange(c->obuf,nwritten,-1) < 0) goto oom; } } } if (done != NULL) *done = (sdslen(c->obuf) == 0); return REDIS_OK; + +oom: + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; +} + +/* Internal helper that returns 1 if the reply was a RESP3 PUSH + * message and we handled it with a user-provided callback. */ +static int redisHandledPushReply(redisContext *c, void *reply) { + if (reply && c->push_cb && redisIsPushReply(reply)) { + c->push_cb(c->privdata, reply); + return 1; + } + + return 0; } -/* Internal helper function to try and get a reply from the reader, - * or set an error in the context otherwise. */ +/* Get a reply from our reader or set an error in the context. */ int redisGetReplyFromReader(redisContext *c, void **reply) { - if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { + if (redisReaderGetReply(c->reader, reply) == REDIS_ERR) { __redisSetError(c,c->reader->err,c->reader->errstr); return REDIS_ERR; } + + return REDIS_OK; +} + +/* Internal helper to get the next reply from our reader while handling + * any PUSH messages we encounter along the way. This is separate from + * redisGetReplyFromReader so as to not change its behavior. */ +static int redisNextInBandReplyFromReader(redisContext *c, void **reply) { + do { + if (redisGetReplyFromReader(c, reply) == REDIS_ERR) + return REDIS_ERR; + } while (redisHandledPushReply(c, *reply)); + return REDIS_OK; } @@ -862,11 +1032,11 @@ int redisGetReply(redisContext *c, void **reply) { void *aux = NULL; /* Try to read pending replies */ - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) + if (redisNextInBandReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; /* For the blocking context, flush output buffer and read reply */ - if (aux == NULL && c->flags & REDIS_BLOCK) { + if (aux == NULL && (c->flags & REDIS_BLOCK)) { /* Write until done */ do { if (redisBufferWrite(c,&wdone) == REDIS_ERR) @@ -877,13 +1047,19 @@ int redisGetReply(redisContext *c, void **reply) { do { if (redisBufferRead(c) == REDIS_ERR) return REDIS_ERR; - if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) + + if (redisNextInBandReplyFromReader(c,&aux) == REDIS_ERR) return REDIS_ERR; } while (aux == NULL); } - /* Set reply object */ - if (reply != NULL) *reply = aux; + /* Set reply or free it if we were passed NULL */ + if (reply != NULL) { + *reply = aux; + } else { + freeReplyObject(aux); + } + return REDIS_OK; } @@ -930,11 +1106,11 @@ int redisvAppendCommand(redisContext *c, const char *format, va_list ap) { } if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { - free(cmd); + hi_free(cmd); return REDIS_ERR; } - free(cmd); + hi_free(cmd); return REDIS_OK; } diff --git a/thirdparty/hiredis/hiredis.h b/thirdparty/hiredis/hiredis.h index 1b0d5e65929..dd29f0b1fc3 100644 --- a/thirdparty/hiredis/hiredis.h +++ b/thirdparty/hiredis/hiredis.h @@ -35,14 +35,20 @@ #define __HIREDIS_H #include "read.h" #include /* for va_list */ +#ifndef _MSC_VER #include /* for struct timeval */ +#else +struct timeval; /* forward declaration */ +typedef long long ssize_t; +#endif #include /* uintXX_t, etc */ #include "sds.h" /* for sds */ +#include "alloc.h" /* for allocation wrappers */ -#define HIREDIS_MAJOR 0 -#define HIREDIS_MINOR 14 -#define HIREDIS_PATCH 0 -#define HIREDIS_SONAME 0.14 +#define HIREDIS_MAJOR 1 +#define HIREDIS_MINOR 0 +#define HIREDIS_PATCH 1 +#define HIREDIS_SONAME 1.0.1-dev /* Connection type can be blocking or non-blocking and is set in the * least significant bit of the flags field in redisContext. */ @@ -74,12 +80,27 @@ /* Flag that is set when we should set SO_REUSEADDR before calling bind() */ #define REDIS_REUSEADDR 0x80 +/** + * Flag that indicates the user does not want the context to + * be automatically freed upon error + */ +#define REDIS_NO_AUTO_FREE 0x200 + #define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */ /* number of times we retry to connect in the case of EADDRNOTAVAIL and * SO_REUSEADDR is being used. */ #define REDIS_CONNECT_RETRIES 10 +/* Forward declarations for structs defined elsewhere */ +struct redisAsyncContext; +struct redisContext; + +/* RESP3 push helpers and callback prototypes */ +#define redisIsPushReply(r) (((redisReply*)(r))->type == REDIS_REPLY_PUSH) +typedef void (redisPushFn)(void *, void *); +typedef void (redisAsyncPushFn)(struct redisAsyncContext *, void *); + #ifdef __cplusplus extern "C" { #endif @@ -88,8 +109,13 @@ extern "C" { typedef struct redisReply { int type; /* REDIS_REPLY_* */ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */ + double dval; /* The double when type is REDIS_REPLY_DOUBLE */ size_t len; /* Length of string */ - char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */ + char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING + REDIS_REPLY_VERB, REDIS_REPLY_DOUBLE (in additional to dval), + and REDIS_REPLY_BIGNUM. */ + char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null + terminated 3 character content type, such as "txt". */ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */ } redisReply; @@ -109,20 +135,113 @@ void redisFreeSdsCommand(sds cmd); enum redisConnectionType { REDIS_CONN_TCP, - REDIS_CONN_UNIX + REDIS_CONN_UNIX, + REDIS_CONN_USERFD }; +struct redisSsl; + +#define REDIS_OPT_NONBLOCK 0x01 +#define REDIS_OPT_REUSEADDR 0x02 + +/** + * Don't automatically free the async object on a connection failure, + * or other implicit conditions. Only free on an explicit call to disconnect() or free() + */ +#define REDIS_OPT_NOAUTOFREE 0x04 + +/* Don't automatically intercept and free RESP3 PUSH replies. */ +#define REDIS_OPT_NO_PUSH_AUTOFREE 0x08 + +/* In Unix systems a file descriptor is a regular signed int, with -1 + * representing an invalid descriptor. In Windows it is a SOCKET + * (32- or 64-bit unsigned integer depending on the architecture), where + * all bits set (~0) is INVALID_SOCKET. */ +#ifndef _WIN32 +typedef int redisFD; +#define REDIS_INVALID_FD -1 +#else +#ifdef _WIN64 +typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */ +#else +typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */ +#endif +#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */ +#endif + +typedef struct { + /* + * the type of connection to use. This also indicates which + * `endpoint` member field to use + */ + int type; + /* bit field of REDIS_OPT_xxx */ + int options; + /* timeout value for connect operation. If NULL, no timeout is used */ + const struct timeval *connect_timeout; + /* timeout value for commands. If NULL, no timeout is used. This can be + * updated at runtime with redisSetTimeout/redisAsyncSetTimeout. */ + const struct timeval *command_timeout; + union { + /** use this field for tcp/ip connections */ + struct { + const char *source_addr; + const char *ip; + int port; + } tcp; + /** use this field for unix domain sockets */ + const char *unix_socket; + /** + * use this field to have hiredis operate an already-open + * file descriptor */ + redisFD fd; + } endpoint; + + /* Optional user defined data/destructor */ + void *privdata; + void (*free_privdata)(void *); + + /* A user defined PUSH message callback */ + redisPushFn *push_cb; + redisAsyncPushFn *async_push_cb; +} redisOptions; + +/** + * Helper macros to initialize options to their specified fields. + */ +#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \ + (opts)->type = REDIS_CONN_TCP; \ + (opts)->endpoint.tcp.ip = ip_; \ + (opts)->endpoint.tcp.port = port_; + +#define REDIS_OPTIONS_SET_UNIX(opts, path) \ + (opts)->type = REDIS_CONN_UNIX; \ + (opts)->endpoint.unix_socket = path; + +#define REDIS_OPTIONS_SET_PRIVDATA(opts, data, dtor) \ + (opts)->privdata = data; \ + (opts)->free_privdata = dtor; \ + +typedef struct redisContextFuncs { + void (*free_privctx)(void *); + ssize_t (*read_)(struct redisContext *, char *, size_t); + ssize_t (*write_)(struct redisContext *); +} redisContextFuncs; + /* Context for a connection to Redis */ typedef struct redisContext { + const redisContextFuncs *funcs; /* Function table */ + int err; /* Error flags, 0 when there is no error */ char errstr[128]; /* String representation of error when applicable */ - int fd; + redisFD fd; int flags; char *obuf; /* Write buffer */ redisReader *reader; /* Protocol reader */ enum redisConnectionType connection_type; - struct timeval *timeout; + struct timeval *connect_timeout; + struct timeval *command_timeout; struct { char *host; @@ -135,10 +254,23 @@ typedef struct redisContext { } unix_sock; /* For non-blocking connect */ - struct sockadr *saddr; + struct sockaddr *saddr; size_t addrlen; + + /* Optional data and corresponding destructor users can use to provide + * context to a given redisContext. Not used by hiredis. */ + void *privdata; + void (*free_privdata)(void *); + + /* Internal context pointer presently used by hiredis to manage + * SSL connections. */ + void *privctx; + + /* An optional RESP3 PUSH handler */ + redisPushFn *push_cb; } redisContext; +redisContext *redisConnectWithOptions(const redisOptions *options); redisContext *redisConnect(const char *ip, int port); redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv); redisContext *redisConnectNonBlock(const char *ip, int port); @@ -149,7 +281,7 @@ redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, redisContext *redisConnectUnix(const char *path); redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv); redisContext *redisConnectUnixNonBlock(const char *path); -redisContext *redisConnectFd(int fd); +redisContext *redisConnectFd(redisFD fd); /** * Reconnect the given context using the saved information. @@ -162,10 +294,11 @@ redisContext *redisConnectFd(int fd); */ int redisReconnect(redisContext *c); +redisPushFn *redisSetPushCallback(redisContext *c, redisPushFn *fn); int redisSetTimeout(redisContext *c, const struct timeval tv); int redisEnableKeepAlive(redisContext *c); void redisFree(redisContext *c); -int redisFreeKeepFd(redisContext *c); +redisFD redisFreeKeepFd(redisContext *c); int redisBufferRead(redisContext *c); int redisBufferWrite(redisContext *c, int *done); diff --git a/thirdparty/hiredis/net.c b/thirdparty/hiredis/net.c index bab9be254af..510df7d44a7 100644 --- a/thirdparty/hiredis/net.c +++ b/thirdparty/hiredis/net.c @@ -59,13 +59,52 @@ /* Defined in hiredis.c */ void __redisSetError(redisContext *c, int type, const char *str); -static void redisContextCloseFd(redisContext *c) { - if (c && c->fd >= 0) { +void redisNetClose(redisContext *c) { + if (c && c->fd != REDIS_INVALID_FD) { close(c->fd); - c->fd = -1; + c->fd = REDIS_INVALID_FD; } } +ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap) { + ssize_t nread = recv(c->fd, buf, bufcap, 0); + if (nread == -1) { + if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { + /* Try again later */ + return 0; + } +#if 0 + else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) { + /* especially in windows */ + __redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout"); + return -1; + } +#endif + else { + __redisSetError(c, REDIS_ERR_IO, NULL); + return -1; + } + } else if (nread == 0) { + __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection"); + return -1; + } else { + return nread; + } +} + +ssize_t redisNetWrite(redisContext *c) { + ssize_t nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0); + if (nwritten < 0) { + if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { + /* Try again later */ + } else { + __redisSetError(c, REDIS_ERR_IO, NULL); + return -1; + } + } + return nwritten; +} + static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) { int errorno = errno; /* snprintf() may change errno */ char buf[128] = { 0 }; @@ -81,15 +120,15 @@ static int redisSetReuseAddr(redisContext *c) { int on = 1; if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } return REDIS_OK; } static int redisCreateSocket(redisContext *c, int type) { - int s; - if ((s = socket(type, SOCK_STREAM, 0)) == -1) { + redisFD s; + if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); return REDIS_ERR; } @@ -104,6 +143,7 @@ static int redisCreateSocket(redisContext *c, int type) { static int redisSetBlocking(redisContext *c, int blocking) { #if 0 +#ifndef _WIN32 int flags; /* Set the socket nonblocking. @@ -111,7 +151,7 @@ static int redisSetBlocking(redisContext *c, int blocking) { * interrupted by a signal. */ if ((flags = fcntl(c->fd, F_GETFL)) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)"); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } @@ -122,16 +162,24 @@ static int redisSetBlocking(redisContext *c, int blocking) { if (fcntl(c->fd, F_SETFL, flags) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)"); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } +#else + u_long mode = blocking ? 0 : 1; + if (ioctl(c->fd, FIONBIO, &mode) == -1) { + __redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)"); + redisNetClose(c); + return REDIS_ERR; + } +#endif /* _WIN32 */ #endif return REDIS_OK; } int redisKeepAlive(redisContext *c, int interval) { int val = 1; - int fd = c->fd; + redisFD fd = c->fd; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){ __redisSetError(c,REDIS_ERR_OTHER,strerror(errno)); @@ -170,11 +218,11 @@ int redisKeepAlive(redisContext *c, int interval) { return REDIS_OK; } -static int redisSetTcpNoDelay(redisContext *c) { +int redisSetTcpNoDelay(redisContext *c) { int yes = 1; if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)"); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } return REDIS_OK; @@ -184,7 +232,7 @@ static int redisSetTcpNoDelay(redisContext *c) { static int redisContextTimeoutMsec(redisContext *c, long *result) { - const struct timeval *timeout = c->timeout; + const struct timeval *timeout = c->connect_timeout; long msec = -1; /* Only use timeout when not NULL. */ @@ -216,12 +264,12 @@ static int redisContextWaitReady(redisContext *c, long msec) { if ((res = poll(wfd, 1, msec)) == -1) { __redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)"); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } else if (res == 0) { errno = ETIMEDOUT; __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } @@ -234,7 +282,7 @@ static int redisContextWaitReady(redisContext *c, long msec) { } __redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL); - redisContextCloseFd(c); + redisNetClose(c); return REDIS_ERR; } @@ -281,21 +329,70 @@ int redisCheckSocketError(redisContext *c) { } int redisContextSetTimeout(redisContext *c, const struct timeval tv) { - if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) { +#if 0 + const void *to_ptr = &tv; + size_t to_sz = sizeof(tv); + + if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); return REDIS_ERR; } - if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) { + if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) { __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)"); return REDIS_ERR; } +#else + double timeout = tv.tv_sec ; + timeout += (double) tv.tv_usec / 1000 / 1000; + if (swoole_coroutine_socket_set_timeout(c->fd, SO_RCVTIMEO, timeout) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); + return REDIS_ERR; + } + if (swoole_coroutine_socket_set_timeout(c->fd, SO_SNDTIMEO, timeout) == -1) { + __redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)"); + return REDIS_ERR; + } +#endif + return REDIS_OK; +} + +int redisContextUpdateConnectTimeout(redisContext *c, const struct timeval *timeout) { + /* Same timeval struct, short circuit */ + if (c->connect_timeout == timeout) + return REDIS_OK; + + /* Allocate context timeval if we need to */ + if (c->connect_timeout == NULL) { + c->connect_timeout = hi_malloc(sizeof(*c->connect_timeout)); + if (c->connect_timeout == NULL) + return REDIS_ERR; + } + + memcpy(c->connect_timeout, timeout, sizeof(*c->connect_timeout)); + return REDIS_OK; +} + +int redisContextUpdateCommandTimeout(redisContext *c, const struct timeval *timeout) { + /* Same timeval struct, short circuit */ + if (c->command_timeout == timeout) + return REDIS_OK; + + /* Allocate context timeval if we need to */ + if (c->command_timeout == NULL) { + c->command_timeout = hi_malloc(sizeof(*c->command_timeout)); + if (c->command_timeout == NULL) + return REDIS_ERR; + } + + memcpy(c->command_timeout, timeout, sizeof(*c->command_timeout)); return REDIS_OK; } static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout, const char *source_addr) { - int s, rv, n; + redisFD s; + int rv, n; char _port[6]; /* strlen("65535"); */ struct addrinfo hints, *servinfo, *bservinfo, *p, *b; int blocking = (c->flags & REDIS_BLOCK); @@ -315,21 +412,19 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, * This is a bit ugly, but atleast it works and doesn't leak memory. **/ if (c->tcp.host != addr) { - free(c->tcp.host); + hi_free(c->tcp.host); - c->tcp.host = strdup(addr); + c->tcp.host = hi_strdup(addr); + if (c->tcp.host == NULL) + goto oom; } if (timeout) { - if (c->timeout != timeout) { - if (c->timeout == NULL) - c->timeout = malloc(sizeof(struct timeval)); - - memcpy(c->timeout, timeout, sizeof(struct timeval)); - } + if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR) + goto oom; } else { - free(c->timeout); - c->timeout = NULL; + hi_free(c->connect_timeout); + c->connect_timeout = NULL; } if (redisContextTimeoutMsec(c, &timeout_msec) != REDIS_OK) { @@ -338,11 +433,11 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, } if (source_addr == NULL) { - free(c->tcp.source_addr); + hi_free(c->tcp.source_addr); c->tcp.source_addr = NULL; } else if (c->tcp.source_addr != source_addr) { - free(c->tcp.source_addr); - c->tcp.source_addr = strdup(source_addr); + hi_free(c->tcp.source_addr); + c->tcp.source_addr = hi_strdup(source_addr); } snprintf(_port, 6, "%d", port); @@ -364,7 +459,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, } for (p = servinfo; p != NULL; p = p->ai_next) { addrretry: - if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1) + if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD) continue; c->fd = s; @@ -405,23 +500,23 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, } /* For repeat connection */ - if (c->saddr) { - free(c->saddr); - } - c->saddr = malloc(p->ai_addrlen); + hi_free(c->saddr); + c->saddr = hi_malloc(p->ai_addrlen); + if (c->saddr == NULL) + goto oom; + memcpy(c->saddr, p->ai_addr, p->ai_addrlen); c->addrlen = p->ai_addrlen; - if (blocking && c->timeout) { - struct pollfd _rfd[1]; - _rfd[0].fd = c->fd; - _rfd[0].events = POLLOUT; - poll(_rfd, 1, (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000)); + if (c->connect_timeout) { + double timeout = c->connect_timeout->tv_sec ; + timeout += (double) c->connect_timeout->tv_usec / 1000 / 1000; + swoole_coroutine_socket_set_connect_timeout(c->fd, timeout); } if (connect(s,p->ai_addr,p->ai_addrlen) == -1) { if (errno == EHOSTUNREACH) { - redisContextCloseFd(c); + redisNetClose(c); continue; } else if (errno == EINPROGRESS) { if (blocking) { @@ -435,19 +530,19 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, if (++reuses >= REDIS_CONNECT_RETRIES) { goto error; } else { - redisContextCloseFd(c); + redisNetClose(c); goto addrretry; } } else { wait_for_ready: if (redisContextWaitReady(c,timeout_msec) != REDIS_OK) goto error; + if (redisSetTcpNoDelay(c) != REDIS_OK) + goto error; } } if (blocking && redisSetBlocking(c,1) != REDIS_OK) goto error; - if (redisSetTcpNoDelay(c) != REDIS_OK) - goto error; c->flags |= REDIS_CONNECTED; rv = REDIS_OK; @@ -460,6 +555,8 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port, goto error; } +oom: + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); error: rv = REDIS_ERR; end: @@ -482,8 +579,9 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port, } int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) { +#ifndef _WIN32 int blocking = (c->flags & REDIS_BLOCK); - struct sockaddr_un sa; + struct sockaddr_un *sa; long timeout_msec = -1; if (redisCreateSocket(c,AF_UNIX) < 0) @@ -492,35 +590,36 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time return REDIS_ERR; c->connection_type = REDIS_CONN_UNIX; - if (c->unix_sock.path != path) - c->unix_sock.path = strdup(path); + if (c->unix_sock.path != path) { + hi_free(c->unix_sock.path); - if (timeout) { - if (c->timeout != timeout) { - if (c->timeout == NULL) - c->timeout = malloc(sizeof(struct timeval)); + c->unix_sock.path = hi_strdup(path); + if (c->unix_sock.path == NULL) + goto oom; + } - memcpy(c->timeout, timeout, sizeof(struct timeval)); - } + if (timeout) { + if (redisContextUpdateConnectTimeout(c, timeout) == REDIS_ERR) + goto oom; } else { - free(c->timeout); - c->timeout = NULL; + hi_free(c->connect_timeout); + c->connect_timeout = NULL; } if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK) return REDIS_ERR; - sa.sun_family = AF_UNIX; - strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1); + /* Don't leak sockaddr if we're reconnecting */ + if (c->saddr) hi_free(c->saddr); - if (blocking && c->timeout) { - struct pollfd _rfd[1]; - _rfd[0].fd = c->fd; - _rfd[0].events = POLLOUT; - poll(_rfd, 1, (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000)); - } + sa = (struct sockaddr_un*)(c->saddr = hi_malloc(sizeof(struct sockaddr_un))); + if (sa == NULL) + goto oom; - if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) { + c->addrlen = sizeof(struct sockaddr_un); + sa->sun_family = AF_UNIX; + strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1); + if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) { if (errno == EINPROGRESS && !blocking) { /* This is ok. */ } else { @@ -535,4 +634,13 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time c->flags |= REDIS_CONNECTED; return REDIS_OK; +#else + /* We currently do not support Unix sockets for Windows. */ + /* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */ + errno = EPROTONOSUPPORT; + return REDIS_ERR; +#endif /* _WIN32 */ +oom: + __redisSetError(c, REDIS_ERR_OOM, "Out of memory"); + return REDIS_ERR; } diff --git a/thirdparty/hiredis/net.h b/thirdparty/hiredis/net.h index a11594e68df..9f43283a5f0 100644 --- a/thirdparty/hiredis/net.h +++ b/thirdparty/hiredis/net.h @@ -37,6 +37,10 @@ #include "hiredis.h" +void redisNetClose(redisContext *c); +ssize_t redisNetRead(redisContext *c, char *buf, size_t bufcap); +ssize_t redisNetWrite(redisContext *c); + int redisCheckSocketError(redisContext *c); int redisContextSetTimeout(redisContext *c, const struct timeval tv); int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout); @@ -47,4 +51,6 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time int redisKeepAlive(redisContext *c, int interval); int redisCheckConnectDone(redisContext *c, int *completed); +int redisSetTcpNoDelay(redisContext *c); + #endif diff --git a/thirdparty/hiredis/read.c b/thirdparty/hiredis/read.c index cc2126778a3..468877c1f11 100644 --- a/thirdparty/hiredis/read.c +++ b/thirdparty/hiredis/read.c @@ -29,21 +29,26 @@ * POSSIBILITY OF SUCH DAMAGE. */ - #include "fmacros.h" #include #include #ifndef _MSC_VER #include +#include #endif #include #include #include #include +#include +#include "alloc.h" #include "read.h" #include "sds.h" +/* Initial size of our nested reply stack and how much we grow it when needd */ +#define REDIS_READER_STACK_SIZE 9 + static void __redisReaderSetError(redisReader *r, int type, const char *str) { size_t len; @@ -117,29 +122,28 @@ static char *readBytes(redisReader *r, unsigned int bytes) { /* Find pointer to \r\n. */ static char *seekNewline(char *s, size_t len) { - int pos = 0; - int _len = len-1; - - /* Position should be < len-1 because the character at "pos" should be - * followed by a \n. Note that strchr cannot be used because it doesn't - * allow to search a limited length and the buffer that is being searched - * might not have a trailing NULL character. */ - while (pos < _len) { - while(pos < _len && s[pos] != '\r') pos++; - if (pos==_len) { - /* Not found. */ - return NULL; - } else { - if (s[pos+1] == '\n') { - /* Found. */ - return s+pos; - } else { - /* Continue searching. */ - pos++; - } + char *ret; + + /* We cannot match with fewer than 2 bytes */ + if (len < 2) + return NULL; + + /* Search up to len - 1 characters */ + len--; + + /* Look for the \r */ + while ((ret = memchr(s, '\r', len)) != NULL) { + if (ret[1] == '\n') { + /* Found. */ + break; } + /* Continue searching. */ + ret++; + len -= ret - s; + s = ret; } - return NULL; + + return ret; } /* Convert a string into a long long. Returns REDIS_OK if the string could be @@ -241,9 +245,12 @@ static void moveToNextTask(redisReader *r) { return; } - cur = &(r->rstack[r->ridx]); - prv = &(r->rstack[r->ridx-1]); - assert(prv->type == REDIS_REPLY_ARRAY); + cur = r->task[r->ridx]; + prv = r->task[r->ridx-1]; + assert(prv->type == REDIS_REPLY_ARRAY || + prv->type == REDIS_REPLY_MAP || + prv->type == REDIS_REPLY_SET || + prv->type == REDIS_REPLY_PUSH); if (cur->idx == prv->elements-1) { r->ridx--; } else { @@ -258,26 +265,113 @@ static void moveToNextTask(redisReader *r) { } static int processLineItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); + redisReadTask *cur = r->task[r->ridx]; void *obj; char *p; int len; if ((p = readLine(r,&len)) != NULL) { if (cur->type == REDIS_REPLY_INTEGER) { + long long v; + + if (string2ll(p, len, &v) == REDIS_ERR) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad integer value"); + return REDIS_ERR; + } + if (r->fn && r->fn->createInteger) { - long long v; - if (string2ll(p, len, &v) == REDIS_ERR) { + obj = r->fn->createInteger(cur,v); + } else { + obj = (void*)REDIS_REPLY_INTEGER; + } + } else if (cur->type == REDIS_REPLY_DOUBLE) { + char buf[326], *eptr; + double d; + + if ((size_t)len >= sizeof(buf)) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Double value is too large"); + return REDIS_ERR; + } + + memcpy(buf,p,len); + buf[len] = '\0'; + + if (len == 3 && strcasecmp(buf,"inf") == 0) { + d = INFINITY; /* Positive infinite. */ + } else if (len == 4 && strcasecmp(buf,"-inf") == 0) { + d = -INFINITY; /* Negative infinite. */ + } else { + d = strtod((char*)buf,&eptr); + /* RESP3 only allows "inf", "-inf", and finite values, while + * strtod() allows other variations on infinity, NaN, + * etc. We explicity handle our two allowed infinite cases + * above, so strtod() should only result in finite values. */ + if (buf[0] == '\0' || eptr != &buf[len] || !isfinite(d)) { __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "Bad integer value"); + "Bad double value"); return REDIS_ERR; } - obj = r->fn->createInteger(cur,v); + } + + if (r->fn && r->fn->createDouble) { + obj = r->fn->createDouble(cur,d,buf,len); } else { - obj = (void*)REDIS_REPLY_INTEGER; + obj = (void*)REDIS_REPLY_DOUBLE; + } + } else if (cur->type == REDIS_REPLY_NIL) { + if (len != 0) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad nil value"); + return REDIS_ERR; + } + + if (r->fn && r->fn->createNil) + obj = r->fn->createNil(cur); + else + obj = (void*)REDIS_REPLY_NIL; + } else if (cur->type == REDIS_REPLY_BOOL) { + int bval; + + if (len != 1 || !strchr("tTfF", p[0])) { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad bool value"); + return REDIS_ERR; + } + + bval = p[0] == 't' || p[0] == 'T'; + if (r->fn && r->fn->createBool) + obj = r->fn->createBool(cur,bval); + else + obj = (void*)REDIS_REPLY_BOOL; + } else if (cur->type == REDIS_REPLY_BIGNUM) { + /* Ensure all characters are decimal digits (with possible leading + * minus sign). */ + int i; + for (i = 0; i < len; i++) { + /* XXX Consider: Allow leading '+'? Error on leading '0's? */ + if (i == 0 && p[0] == '-') continue; + if (p[i] < '0' || p[i] > '9') { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad bignum value"); + return REDIS_ERR; + } } + if (r->fn && r->fn->createString) + obj = r->fn->createString(cur,p,len); + else + obj = (void*)REDIS_REPLY_BIGNUM; } else { /* Type will be error or status. */ + int i; + for (i = 0; i < len; i++) { + if (p[i] == '\r' || p[i] == '\n') { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Bad simple string value"); + return REDIS_ERR; + } + } if (r->fn && r->fn->createString) obj = r->fn->createString(cur,p,len); else @@ -299,7 +393,7 @@ static int processLineItem(redisReader *r) { } static int processBulkItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); + redisReadTask *cur = r->task[r->ridx]; void *obj = NULL; char *p, *s; long long len; @@ -335,10 +429,18 @@ static int processBulkItem(redisReader *r) { /* Only continue when the buffer contains the entire bulk item. */ bytelen += len+2; /* include \r\n */ if (r->pos+bytelen <= r->len) { + if ((cur->type == REDIS_REPLY_VERB && len < 4) || + (cur->type == REDIS_REPLY_VERB && s[5] != ':')) + { + __redisReaderSetError(r,REDIS_ERR_PROTOCOL, + "Verbatim string 4 bytes of content type are " + "missing or incorrectly encoded."); + return REDIS_ERR; + } if (r->fn && r->fn->createString) obj = r->fn->createString(cur,s+2,len); else - obj = (void*)REDIS_REPLY_STRING; + obj = (void*)(long)cur->type; success = 1; } } @@ -362,18 +464,42 @@ static int processBulkItem(redisReader *r) { return REDIS_ERR; } -static int processMultiBulkItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); +static int redisReaderGrow(redisReader *r) { + redisReadTask **aux; + int newlen; + + /* Grow our stack size */ + newlen = r->tasks + REDIS_READER_STACK_SIZE; + aux = hi_realloc(r->task, sizeof(*r->task) * newlen); + if (aux == NULL) + goto oom; + + r->task = aux; + + /* Allocate new tasks */ + for (; r->tasks < newlen; r->tasks++) { + r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); + if (r->task[r->tasks] == NULL) + goto oom; + } + + return REDIS_OK; +oom: + __redisReaderSetErrorOOM(r); + return REDIS_ERR; +} + +/* Process the array, map and set types. */ +static int processAggregateItem(redisReader *r) { + redisReadTask *cur = r->task[r->ridx]; void *obj; char *p; long long elements; int root = 0, len; - /* Set error for nested multi bulks with depth > 7 */ - if (r->ridx == 8) { - __redisReaderSetError(r,REDIS_ERR_PROTOCOL, - "No support for nested multi bulk replies with depth > 7"); - return REDIS_ERR; + if (r->ridx == r->tasks - 1) { + if (redisReaderGrow(r) == REDIS_ERR) + return REDIS_ERR; } if ((p = readLine(r,&len)) != NULL) { @@ -385,7 +511,9 @@ static int processMultiBulkItem(redisReader *r) { root = (r->ridx == 0); - if (elements < -1 || elements > INT_MAX) { + if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX) || + (r->maxelements > 0 && elements > r->maxelements)) + { __redisReaderSetError(r,REDIS_ERR_PROTOCOL, "Multi-bulk length out of range"); return REDIS_ERR; @@ -404,10 +532,12 @@ static int processMultiBulkItem(redisReader *r) { moveToNextTask(r); } else { + if (cur->type == REDIS_REPLY_MAP) elements *= 2; + if (r->fn && r->fn->createArray) obj = r->fn->createArray(cur,elements); else - obj = (void*)REDIS_REPLY_ARRAY; + obj = (void*)(long)cur->type; if (obj == NULL) { __redisReaderSetErrorOOM(r); @@ -419,12 +549,12 @@ static int processMultiBulkItem(redisReader *r) { cur->elements = elements; cur->obj = obj; r->ridx++; - r->rstack[r->ridx].type = -1; - r->rstack[r->ridx].elements = -1; - r->rstack[r->ridx].idx = 0; - r->rstack[r->ridx].obj = NULL; - r->rstack[r->ridx].parent = cur; - r->rstack[r->ridx].privdata = r->privdata; + r->task[r->ridx]->type = -1; + r->task[r->ridx]->elements = -1; + r->task[r->ridx]->idx = 0; + r->task[r->ridx]->obj = NULL; + r->task[r->ridx]->parent = cur; + r->task[r->ridx]->privdata = r->privdata; } else { moveToNextTask(r); } @@ -439,7 +569,7 @@ static int processMultiBulkItem(redisReader *r) { } static int processItem(redisReader *r) { - redisReadTask *cur = &(r->rstack[r->ridx]); + redisReadTask *cur = r->task[r->ridx]; char *p; /* check if we need to read type */ @@ -455,12 +585,36 @@ static int processItem(redisReader *r) { case ':': cur->type = REDIS_REPLY_INTEGER; break; + case ',': + cur->type = REDIS_REPLY_DOUBLE; + break; + case '_': + cur->type = REDIS_REPLY_NIL; + break; case '$': cur->type = REDIS_REPLY_STRING; break; case '*': cur->type = REDIS_REPLY_ARRAY; break; + case '%': + cur->type = REDIS_REPLY_MAP; + break; + case '~': + cur->type = REDIS_REPLY_SET; + break; + case '#': + cur->type = REDIS_REPLY_BOOL; + break; + case '=': + cur->type = REDIS_REPLY_VERB; + break; + case '>': + cur->type = REDIS_REPLY_PUSH; + break; + case '(': + cur->type = REDIS_REPLY_BIGNUM; + break; default: __redisReaderSetErrorProtocolByte(r,*p); return REDIS_ERR; @@ -476,11 +630,19 @@ static int processItem(redisReader *r) { case REDIS_REPLY_ERROR: case REDIS_REPLY_STATUS: case REDIS_REPLY_INTEGER: + case REDIS_REPLY_DOUBLE: + case REDIS_REPLY_NIL: + case REDIS_REPLY_BOOL: + case REDIS_REPLY_BIGNUM: return processLineItem(r); case REDIS_REPLY_STRING: + case REDIS_REPLY_VERB: return processBulkItem(r); case REDIS_REPLY_ARRAY: - return processMultiBulkItem(r); + case REDIS_REPLY_MAP: + case REDIS_REPLY_SET: + case REDIS_REPLY_PUSH: + return processAggregateItem(r); default: assert(NULL); return REDIS_ERR; /* Avoid warning. */ @@ -490,29 +652,53 @@ static int processItem(redisReader *r) { redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn) { redisReader *r; - r = calloc(1,sizeof(redisReader)); + r = hi_calloc(1,sizeof(redisReader)); if (r == NULL) return NULL; - r->fn = fn; r->buf = sdsempty(); - r->maxbuf = REDIS_READER_MAX_BUF; - if (r->buf == NULL) { - free(r); - return NULL; + if (r->buf == NULL) + goto oom; + + r->task = hi_calloc(REDIS_READER_STACK_SIZE, sizeof(*r->task)); + if (r->task == NULL) + goto oom; + + for (; r->tasks < REDIS_READER_STACK_SIZE; r->tasks++) { + r->task[r->tasks] = hi_calloc(1, sizeof(**r->task)); + if (r->task[r->tasks] == NULL) + goto oom; } + r->fn = fn; + r->maxbuf = REDIS_READER_MAX_BUF; + r->maxelements = REDIS_READER_MAX_ARRAY_ELEMENTS; r->ridx = -1; + return r; +oom: + redisReaderFree(r); + return NULL; } void redisReaderFree(redisReader *r) { if (r == NULL) return; + if (r->reply != NULL && r->fn && r->fn->freeObject) r->fn->freeObject(r->reply); + + if (r->task) { + /* We know r->task[i] is allocated if i < r->tasks */ + for (int i = 0; i < r->tasks; i++) { + hi_free(r->task[i]); + } + + hi_free(r->task); + } + sdsfree(r->buf); - free(r); + hi_free(r); } int redisReaderFeed(redisReader *r, const char *buf, size_t len) { @@ -528,23 +714,22 @@ int redisReaderFeed(redisReader *r, const char *buf, size_t len) { if (r->len == 0 && r->maxbuf != 0 && sdsavail(r->buf) > r->maxbuf) { sdsfree(r->buf); r->buf = sdsempty(); - r->pos = 0; + if (r->buf == 0) goto oom; - /* r->buf should not be NULL since we just free'd a larger one. */ - assert(r->buf != NULL); + r->pos = 0; } newbuf = sdscatlen(r->buf,buf,len); - if (newbuf == NULL) { - __redisReaderSetErrorOOM(r); - return REDIS_ERR; - } + if (newbuf == NULL) goto oom; r->buf = newbuf; r->len = sdslen(r->buf); } return REDIS_OK; +oom: + __redisReaderSetErrorOOM(r); + return REDIS_ERR; } int redisReaderGetReply(redisReader *r, void **reply) { @@ -562,12 +747,12 @@ int redisReaderGetReply(redisReader *r, void **reply) { /* Set first item to process when the stack is empty. */ if (r->ridx == -1) { - r->rstack[0].type = -1; - r->rstack[0].elements = -1; - r->rstack[0].idx = -1; - r->rstack[0].obj = NULL; - r->rstack[0].parent = NULL; - r->rstack[0].privdata = r->privdata; + r->task[0]->type = -1; + r->task[0]->elements = -1; + r->task[0]->idx = -1; + r->task[0]->obj = NULL; + r->task[0]->parent = NULL; + r->task[0]->privdata = r->privdata; r->ridx = 0; } @@ -583,15 +768,18 @@ int redisReaderGetReply(redisReader *r, void **reply) { /* Discard part of the buffer when we've consumed at least 1k, to avoid * doing unnecessary calls to memmove() in sds.c. */ if (r->pos >= 1024) { - sdsrange(r->buf,r->pos,-1); + if (sdsrange(r->buf,r->pos,-1) < 0) return REDIS_ERR; r->pos = 0; r->len = sdslen(r->buf); } /* Emit a reply when there is one. */ if (r->ridx == -1) { - if (reply != NULL) + if (reply != NULL) { *reply = r->reply; + } else if (r->reply != NULL && r->fn && r->fn->freeObject) { + r->fn->freeObject(r->reply); + } r->reply = NULL; } return REDIS_OK; diff --git a/thirdparty/hiredis/read.h b/thirdparty/hiredis/read.h index 2988aa453bd..2d74d77a5b4 100644 --- a/thirdparty/hiredis/read.h +++ b/thirdparty/hiredis/read.h @@ -45,6 +45,7 @@ #define REDIS_ERR_EOF 3 /* End of file */ #define REDIS_ERR_PROTOCOL 4 /* Protocol error */ #define REDIS_ERR_OOM 5 /* Out of memory */ +#define REDIS_ERR_TIMEOUT 6 /* Timed out */ #define REDIS_ERR_OTHER 2 /* Everything else... */ #define REDIS_REPLY_STRING 1 @@ -53,8 +54,20 @@ #define REDIS_REPLY_NIL 4 #define REDIS_REPLY_STATUS 5 #define REDIS_REPLY_ERROR 6 +#define REDIS_REPLY_DOUBLE 7 +#define REDIS_REPLY_BOOL 8 +#define REDIS_REPLY_MAP 9 +#define REDIS_REPLY_SET 10 +#define REDIS_REPLY_ATTR 11 +#define REDIS_REPLY_PUSH 12 +#define REDIS_REPLY_BIGNUM 13 +#define REDIS_REPLY_VERB 14 -#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */ +/* Default max unused reader buffer. */ +#define REDIS_READER_MAX_BUF (1024*16) + +/* Default multi-bulk element limit */ +#define REDIS_READER_MAX_ARRAY_ELEMENTS ((1LL<<32) - 1) #ifdef __cplusplus extern "C" { @@ -62,7 +75,7 @@ extern "C" { typedef struct redisReadTask { int type; - int elements; /* number of elements in multibulk container */ + long long elements; /* number of elements in multibulk container */ int idx; /* index in parent (array) object */ void *obj; /* holds user-generated value for a read task */ struct redisReadTask *parent; /* parent task */ @@ -71,9 +84,11 @@ typedef struct redisReadTask { typedef struct redisReplyObjectFunctions { void *(*createString)(const redisReadTask*, char*, size_t); - void *(*createArray)(const redisReadTask*, int); + void *(*createArray)(const redisReadTask*, size_t); void *(*createInteger)(const redisReadTask*, long long); + void *(*createDouble)(const redisReadTask*, double, char*, size_t); void *(*createNil)(const redisReadTask*); + void *(*createBool)(const redisReadTask*, int); void (*freeObject)(void*); } redisReplyObjectFunctions; @@ -85,8 +100,11 @@ typedef struct redisReader { size_t pos; /* Buffer cursor */ size_t len; /* Buffer length */ size_t maxbuf; /* Max length of unused buffer */ + long long maxelements; /* Max multi-bulk elements */ + + redisReadTask **task; + int tasks; - redisReadTask rstack[9]; int ridx; /* Index of current read task */ void *reply; /* Temporary reply pointer */ diff --git a/thirdparty/hiredis/sds.c b/thirdparty/hiredis/sds.c index 44777b10c65..35baa057eb9 100644 --- a/thirdparty/hiredis/sds.c +++ b/thirdparty/hiredis/sds.c @@ -30,11 +30,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include "fmacros.h" #include #include #include #include #include +#include #include "sds.h" #include "sdsalloc.h" @@ -70,7 +72,7 @@ static inline char sdsReqType(size_t string_size) { * and 'initlen'. * If NULL is used for 'init' the string is initialized with zero bytes. * - * The string is always null-termined (all the sds strings are, always) so + * The string is always null-terminated (all the sds strings are, always) so * even if you create an sds string with: * * mystring = sdsnewlen("abc",3); @@ -219,10 +221,7 @@ sds sdsMakeRoomFor(sds s, size_t addlen) { hdrlen = sdsHdrSize(type); if (oldtype==type) { newsh = s_realloc(sh, hdrlen+newlen+1); - if (newsh == NULL) { - s_free(sh); - return NULL; - } + if (newsh == NULL) return NULL; s = (char*)newsh+hdrlen; } else { /* Since the header size changes, need to move the string forward, @@ -416,7 +415,7 @@ sds sdscpylen(sds s, const char *t, size_t len) { return s; } -/* Like sdscpylen() but 't' must be a null-termined string so that the length +/* Like sdscpylen() but 't' must be a null-terminated string so that the length * of the string is obtained with strlen(). */ sds sdscpy(sds s, const char *t) { return sdscpylen(s, t, strlen(t)); @@ -715,15 +714,20 @@ sds sdstrim(sds s, const char *cset) { * * The string is modified in-place. * + * Return value: + * -1 (error) if sdslen(s) is larger than maximum positive ssize_t value. + * 0 on success. + * * Example: * * s = sdsnew("Hello World"); * sdsrange(s,1,-1); => "ello World" */ -void sdsrange(sds s, int start, int end) { +int sdsrange(sds s, ssize_t start, ssize_t end) { size_t newlen, len = sdslen(s); + if (len > SSIZE_MAX) return -1; - if (len == 0) return; + if (len == 0) return 0; if (start < 0) { start = len+start; if (start < 0) start = 0; @@ -734,9 +738,9 @@ void sdsrange(sds s, int start, int end) { } newlen = (start > end) ? 0 : (end-start)+1; if (newlen != 0) { - if (start >= (signed)len) { + if (start >= (ssize_t)len) { newlen = 0; - } else if (end >= (signed)len) { + } else if (end >= (ssize_t)len) { end = len-1; newlen = (start > end) ? 0 : (end-start)+1; } @@ -746,6 +750,7 @@ void sdsrange(sds s, int start, int end) { if (start && newlen) memmove(s, s+start, newlen); s[newlen] = 0; sdssetlen(s,newlen); + return 0; } /* Apply tolower() to every character of the sds string 's'. */ @@ -889,13 +894,6 @@ sds sdscatrepr(sds s, const char *p, size_t len) { return sdscatlen(s,"\"",1); } -/* Helper function for sdssplitargs() that returns non zero if 'c' - * is a valid hex digit. */ -int is_hex_digit(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || - (c >= 'A' && c <= 'F'); -} - /* Helper function for sdssplitargs() that converts a hex digit into an * integer from 0 to 15 */ int hex_digit_to_int(char c) { @@ -958,8 +956,8 @@ sds *sdssplitargs(const char *line, int *argc) { while(!done) { if (inq) { if (*p == '\\' && *(p+1) == 'x' && - is_hex_digit(*(p+2)) && - is_hex_digit(*(p+3))) + isxdigit(*(p+2)) && + isxdigit(*(p+3))) { unsigned char byte; @@ -1035,7 +1033,7 @@ sds *sdssplitargs(const char *line, int *argc) { s_free(vector); return NULL; } - + vector = new_vector; vector[*argc] = current; (*argc)++; diff --git a/thirdparty/hiredis/sds.h b/thirdparty/hiredis/sds.h index 13be75a9fca..eda8833b598 100644 --- a/thirdparty/hiredis/sds.h +++ b/thirdparty/hiredis/sds.h @@ -34,6 +34,11 @@ #define __SDS_H #define SDS_MAX_PREALLOC (1024*1024) +#ifdef _MSC_VER +#define __attribute__(x) +typedef long long ssize_t; +#define SSIZE_MAX (LLONG_MAX >> 1) +#endif #include #include @@ -132,20 +137,20 @@ static inline void sdssetlen(sds s, size_t newlen) { case SDS_TYPE_5: { unsigned char *fp = ((unsigned char*)s)-1; - *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); + *fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS)); } break; case SDS_TYPE_8: - SDS_HDR(8,s)->len = newlen; + SDS_HDR(8,s)->len = (uint8_t)newlen; break; case SDS_TYPE_16: - SDS_HDR(16,s)->len = newlen; + SDS_HDR(16,s)->len = (uint16_t)newlen; break; case SDS_TYPE_32: - SDS_HDR(32,s)->len = newlen; + SDS_HDR(32,s)->len = (uint32_t)newlen; break; case SDS_TYPE_64: - SDS_HDR(64,s)->len = newlen; + SDS_HDR(64,s)->len = (uint64_t)newlen; break; } } @@ -156,21 +161,21 @@ static inline void sdsinclen(sds s, size_t inc) { case SDS_TYPE_5: { unsigned char *fp = ((unsigned char*)s)-1; - unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc; + unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc; *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS); } break; case SDS_TYPE_8: - SDS_HDR(8,s)->len += inc; + SDS_HDR(8,s)->len += (uint8_t)inc; break; case SDS_TYPE_16: - SDS_HDR(16,s)->len += inc; + SDS_HDR(16,s)->len += (uint16_t)inc; break; case SDS_TYPE_32: - SDS_HDR(32,s)->len += inc; + SDS_HDR(32,s)->len += (uint32_t)inc; break; case SDS_TYPE_64: - SDS_HDR(64,s)->len += inc; + SDS_HDR(64,s)->len += (uint64_t)inc; break; } } @@ -200,16 +205,16 @@ static inline void sdssetalloc(sds s, size_t newlen) { /* Nothing to do, this type has no total allocation info. */ break; case SDS_TYPE_8: - SDS_HDR(8,s)->alloc = newlen; + SDS_HDR(8,s)->alloc = (uint8_t)newlen; break; case SDS_TYPE_16: - SDS_HDR(16,s)->alloc = newlen; + SDS_HDR(16,s)->alloc = (uint16_t)newlen; break; case SDS_TYPE_32: - SDS_HDR(32,s)->alloc = newlen; + SDS_HDR(32,s)->alloc = (uint32_t)newlen; break; case SDS_TYPE_64: - SDS_HDR(64,s)->alloc = newlen; + SDS_HDR(64,s)->alloc = (uint64_t)newlen; break; } } @@ -236,7 +241,7 @@ sds sdscatprintf(sds s, const char *fmt, ...); sds sdscatfmt(sds s, char const *fmt, ...); sds sdstrim(sds s, const char *cset); -void sdsrange(sds s, int start, int end); +int sdsrange(sds s, ssize_t start, ssize_t end); void sdsupdatelen(sds s); void sdsclear(sds s); int sdscmp(const sds s1, const sds s2); diff --git a/thirdparty/hiredis/sdsalloc.h b/thirdparty/hiredis/sdsalloc.h index f43023c4843..5538dd94c97 100644 --- a/thirdparty/hiredis/sdsalloc.h +++ b/thirdparty/hiredis/sdsalloc.h @@ -37,6 +37,8 @@ * the include of your alternate allocator if needed (not needed in order * to use the default libc allocator). */ -#define s_malloc malloc -#define s_realloc realloc -#define s_free free +#include "alloc.h" + +#define s_malloc hi_malloc +#define s_realloc hi_realloc +#define s_free hi_free 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/COPYING b/thirdparty/nghttp2/COPYING new file mode 100644 index 00000000000..80201792ec7 --- /dev/null +++ b/thirdparty/nghttp2/COPYING @@ -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. diff --git a/thirdparty/nghttp2/LICENSE b/thirdparty/nghttp2/LICENSE new file mode 100644 index 00000000000..d9e4eb197b9 --- /dev/null +++ b/thirdparty/nghttp2/LICENSE @@ -0,0 +1 @@ +See COPYING diff --git a/thirdparty/nghttp2/nghttp2.h b/thirdparty/nghttp2/nghttp2.h index 55617d05484..29f12ac79fa 100644 --- a/thirdparty/nghttp2/nghttp2.h +++ b/thirdparty/nghttp2/nghttp2.h @@ -28,7 +28,12 @@ /* Define WIN32 when build target is Win32 API (borrowed from libcurl) */ #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) -#define WIN32 +# define WIN32 +#endif + +/* Compatibility for non-Clang compilers */ +#ifndef __has_declspec_attribute +# define __has_declspec_attribute(x) 0 #endif #ifdef __cplusplus @@ -40,46 +45,57 @@ extern "C" { /* MSVC < 2013 does not have inttypes.h because it is not C99 compliant. See compiler macros and version number in https://sourceforge.net/p/predef/wiki/Compilers/ */ -#include +# include #else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ -#include +# include #endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ #include #include #include +#include +const char *nghttp2_strerror(int error_code); static inline uint8_t *nghttp2_cpymem(uint8_t *dest, const void *src, size_t len) { - if (len == 0) { - return dest; - } + if (len == 0) { + return dest; + } - memcpy(dest, src, len); + memcpy(dest, src, len); - return dest + len; + return dest + len; } -#define nghttp2_min(A, B) ((A) < (B) ? (A) : (B)) -#define nghttp2_max(A, B) ((A) > (B) ? (A) : (B)) - #define DEBUGF(s, ...) - -const char *nghttp2_strerror(int error_code); +#define nghttp2_min_size(A, B) ((A) < (B) ? (A) : (B)) +#define nghttp2_max_size(A, B) ((A) > (B) ? (A) : (B)) #ifdef NGHTTP2_STATICLIB -#define NGHTTP2_EXTERN -#elif defined(WIN32) -#ifdef BUILDING_NGHTTP2 -#define NGHTTP2_EXTERN __declspec(dllexport) -#else /* !BUILDING_NGHTTP2 */ -#define NGHTTP2_EXTERN __declspec(dllimport) -#endif /* !BUILDING_NGHTTP2 */ -#else /* !defined(WIN32) */ +# define NGHTTP2_EXTERN +#elif defined(WIN32) || \ + (__has_declspec_attribute(dllexport) && __has_declspec_attribute(dllimport)) +# ifdef BUILDING_NGHTTP2 +# define NGHTTP2_EXTERN __declspec(dllexport) +# else /* !BUILDING_NGHTTP2 */ +# define NGHTTP2_EXTERN __declspec(dllimport) +# endif /* !BUILDING_NGHTTP2 */ +#else /* !defined(WIN32) */ +# ifdef BUILDING_NGHTTP2 +# define NGHTTP2_EXTERN __attribute__((visibility("default"))) +# else /* !BUILDING_NGHTTP2 */ +# define NGHTTP2_EXTERN +# endif /* !BUILDING_NGHTTP2 */ +#endif /* !defined(WIN32) */ + #ifdef BUILDING_NGHTTP2 -#define NGHTTP2_EXTERN __attribute__((visibility("default"))) -#else /* !BUILDING_NGHTTP2 */ -#define NGHTTP2_EXTERN -#endif /* !BUILDING_NGHTTP2 */ -#endif /* !defined(WIN32) */ +# undef NGHTTP2_NO_SSIZE_T +#endif /* BUILDING_NGHTTP2 */ + +/** + * @typedef + * + * :type:`nghttp2_ssize` is a signed counterpart of size_t. + */ +typedef ptrdiff_t nghttp2_ssize; /** * @macro @@ -129,6 +145,15 @@ const char *nghttp2_strerror(int error_code); */ #define NGHTTP2_CLEARTEXT_PROTO_VERSION_ID_LEN 3 +struct nghttp2_session; +/** + * @struct + * + * The primary structure to hold the resources needed for a HTTP/2 + * session. The details of this structure are intentionally hidden + * from the public API. + */ +typedef struct nghttp2_session nghttp2_session; /** * @macro @@ -169,6 +194,12 @@ typedef struct { /** * @macro * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The default weight of stream dependency. */ #define NGHTTP2_DEFAULT_WEIGHT 16 @@ -176,6 +207,12 @@ typedef struct { /** * @macro * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The maximum weight of stream dependency. */ #define NGHTTP2_MAX_WEIGHT 256 @@ -183,6 +220,12 @@ typedef struct { /** * @macro * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The minimum weight of stream dependency. */ #define NGHTTP2_MIN_WEIGHT 1 @@ -229,6 +272,13 @@ typedef struct { */ #define NGHTTP2_CLIENT_MAGIC_LEN 24 +/** + * @macro + * + * The default max number of settings per SETTINGS frame + */ +#define NGHTTP2_DEFAULT_MAX_SETTINGS 32 + /** * @enum * @@ -249,7 +299,7 @@ typedef enum { */ NGHTTP2_ERR_UNSUPPORTED_VERSION = -503, /** - * Used as a return value from :type:`nghttp2_send_callback`, + * Used as a return value from :type:`nghttp2_send_callback2`, * :type:`nghttp2_recv_callback` and * :type:`nghttp2_send_data_callback` to indicate that the operation * would block. @@ -269,9 +319,9 @@ typedef enum { NGHTTP2_ERR_EOF = -507, /** * Used as a return value from - * :func:`nghttp2_data_source_read_callback` to indicate that data + * :func:`nghttp2_data_source_read_callback2` to indicate that data * transfer is postponed. See - * :func:`nghttp2_data_source_read_callback` for details. + * :func:`nghttp2_data_source_read_callback2` for details. */ NGHTTP2_ERR_DEFERRED = -508, /** @@ -395,12 +445,22 @@ typedef enum { */ NGHTTP2_ERR_CANCEL = -535, /** - * The errors < :enum:`NGHTTP2_ERR_FATAL` mean that the library is - * under unexpected condition and processing was terminated (e.g., - * out of memory). If application receives this error code, it must - * stop using that :type:`nghttp2_session` object and only allowed - * operation for that object is deallocate it using - * `nghttp2_session_del()`. + * When a local endpoint expects to receive SETTINGS frame, it + * receives an other type of frame. + */ + NGHTTP2_ERR_SETTINGS_EXPECTED = -536, + /** + * When a local endpoint receives too many settings entries + * in a single SETTINGS frame. + */ + NGHTTP2_ERR_TOO_MANY_SETTINGS = -537, + /** + * The errors < :enum:`nghttp2_error.NGHTTP2_ERR_FATAL` mean that + * the library is under unexpected condition and processing was + * terminated (e.g., out of memory). If application receives this + * error code, it must stop using that :type:`nghttp2_session` + * object and only allowed operation for that object is deallocate + * it using `nghttp2_session_del()`. */ NGHTTP2_ERR_FATAL = -900, /** @@ -424,7 +484,12 @@ typedef enum { * exhaustion on server side to send these frames forever and does * not read network. */ - NGHTTP2_ERR_FLOODED = -904 + NGHTTP2_ERR_FLOODED = -904, + /** + * When a local endpoint receives too many CONTINUATION frames + * following a HEADER frame. + */ + NGHTTP2_ERR_TOO_MANY_CONTINUATIONS = -905, } nghttp2_error; /** @@ -476,6 +541,15 @@ NGHTTP2_EXTERN void nghttp2_rcbuf_decref(nghttp2_rcbuf *rcbuf); */ NGHTTP2_EXTERN nghttp2_vec nghttp2_rcbuf_get_buf(nghttp2_rcbuf *rcbuf); +/** + * @function + * + * Returns nonzero if the underlying buffer is statically allocated, + * and 0 otherwise. This can be useful for language bindings that wish + * to avoid creating duplicate strings for these buffers. + */ +NGHTTP2_EXTERN int nghttp2_rcbuf_is_static(const nghttp2_rcbuf *rcbuf); + /** * @enum * @@ -520,9 +594,9 @@ typedef struct { * :type:`nghttp2_on_frame_send_callback`, and * :type:`nghttp2_on_frame_not_send_callback`), it may not be * NULL-terminated if header field is passed from application with - * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_NAME`). When application - * is constructing this struct, |name| is not required to be - * NULL-terminated. + * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`). + * When application is constructing this struct, |name| is not + * required to be NULL-terminated. */ uint8_t *name; /** @@ -533,9 +607,9 @@ typedef struct { * :type:`nghttp2_on_frame_send_callback`, and * :type:`nghttp2_on_frame_not_send_callback`), it may not be * NULL-terminated if header field is passed from application with - * the flag :enum:`NGHTTP2_NV_FLAG_NO_COPY_VALUE`). When - * application is constructing this struct, |value| is not required - * to be NULL-terminated. + * the flag :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE`). + * When application is constructing this struct, |value| is not + * required to be NULL-terminated. */ uint8_t *value; /** @@ -604,7 +678,16 @@ typedef enum { * The ALTSVC frame, which is defined in `RFC 7383 * `_. */ - NGHTTP2_ALTSVC = 0x0a + NGHTTP2_ALTSVC = 0x0a, + /** + * The ORIGIN frame, which is defined by `RFC 8336 + * `_. + */ + NGHTTP2_ORIGIN = 0x0c, + /** + * The PRIORITY_UPDATE frame, which is defined by :rfc:`9218`. + */ + NGHTTP2_PRIORITY_UPDATE = 0x10 } nghttp2_frame_type; /** @@ -668,7 +751,16 @@ typedef enum { /** * SETTINGS_MAX_HEADER_LIST_SIZE */ - NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06 + NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x06, + /** + * SETTINGS_ENABLE_CONNECT_PROTOCOL + * (`RFC 8441 `_) + */ + NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL = 0x08, + /** + * SETTINGS_NO_RFC7540_PRIORITIES (:rfc:`9218`) + */ + NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES = 0x09 } nghttp2_settings_id; /* Note: If we add SETTINGS, update the capacity of NGHTTP2_INBOUND_NUM_IV as well */ @@ -682,8 +774,8 @@ typedef enum { * * Default maximum number of incoming concurrent streams. Use * `nghttp2_submit_settings()` with - * :enum:`NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS` to change the - * maximum number of incoming concurrent streams. + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS` + * to change the maximum number of incoming concurrent streams. * * .. note:: * @@ -787,7 +879,7 @@ typedef struct { * @union * * This union represents the some kind of data source passed to - * :type:`nghttp2_data_source_read_callback`. + * :type:`nghttp2_data_source_read_callback2`. */ typedef union { /** @@ -804,7 +896,7 @@ typedef union { * @enum * * The flags used to set in |data_flags| output parameter in - * :type:`nghttp2_data_source_read_callback`. + * :type:`nghttp2_data_source_read_callback2`. */ typedef enum { /** @@ -818,8 +910,8 @@ typedef enum { /** * Indicates that END_STREAM flag must not be set even if * NGHTTP2_DATA_FLAG_EOF is set. Usually this flag is used to send - * trailer fields with `nghttp2_submit_request()` or - * `nghttp2_submit_response()`. + * trailer fields with `nghttp2_submit_request2()` or + * `nghttp2_submit_response2()`. */ NGHTTP2_DATA_FLAG_NO_END_STREAM = 0x02, /** @@ -829,63 +921,188 @@ typedef enum { NGHTTP2_DATA_FLAG_NO_COPY = 0x04 } nghttp2_data_flag; - +#ifndef NGHTTP2_NO_SSIZE_T /** * @functypedef * - * Custom memory allocator to replace malloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. - */ -typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); - -/** - * @functypedef + * .. warning:: * - * Custom memory allocator to replace free(). The |mem_user_data| is - * the mem_user_data member of :type:`nghttp2_mem` structure. + * Deprecated. Use :type:`nghttp2_data_source_read_callback2` + * instead. + * + * Callback function invoked when the library wants to read data from + * the |source|. The read data is sent in the stream |stream_id|. + * The implementation of this function must read at most |length| + * bytes of data from |source| (or possibly other places) and store + * them in |buf| and return number of data stored in |buf|. If EOF is + * reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag + * in |*data_flags|. + * + * Sometime it is desirable to avoid copying data into |buf| and let + * application to send data directly. To achieve this, set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to + * |*data_flags| (and possibly other flags, just like when we do + * copy), and return the number of bytes to send without copying data + * into |buf|. The library, seeing + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + * :type:`nghttp2_send_data_callback`. The application must send + * complete DATA frame in that callback. + * + * If this callback is set by `nghttp2_submit_request()`, + * `nghttp2_submit_response()` or `nghttp2_submit_headers()` and + * `nghttp2_submit_data()` with flag parameter + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to + * |*data_flags|, DATA frame will have END_STREAM flag set. Usually, + * this is expected behaviour and all are fine. One exception is send + * trailer fields. You cannot send trailer fields after sending frame + * with END_STREAM set. To avoid this problem, one can set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along + * with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the + * library not to set END_STREAM in DATA frame. Then application can + * use `nghttp2_submit_trailer()` to send trailer fields. + * `nghttp2_submit_trailer()` can be called inside this callback. + * + * If the application wants to postpone DATA frames (e.g., + * asynchronous I/O, or reading data blocks for long time), it is + * achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED` + * without reading any data in this invocation. The library removes + * DATA frame from the outgoing queue temporarily. To move back + * deferred DATA frame to outgoing queue, call + * `nghttp2_session_resume_data()`. + * + * By default, |length| is limited to 16KiB at maximum. If peer + * allows larger frames, application can enlarge transmission buffer + * size. See :type:`nghttp2_data_source_read_length_callback` for + * more details. + * + * If the application just wants to return from + * `nghttp2_session_send()` or `nghttp2_session_mem_send()` without + * sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. + * + * In case of error, there are 2 choices. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will + * close the stream by issuing RST_STREAM with + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. If a different + * error code is desirable, use `nghttp2_submit_rst_stream()` with a + * desired error code and then return + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will + * signal the entire session failure. */ -typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); +typedef ssize_t (*nghttp2_data_source_read_callback)( + nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, + uint32_t *data_flags, nghttp2_data_source *source, void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ /** * @functypedef * - * Custom memory allocator to replace calloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. + * Callback function invoked when the library wants to read data from + * the |source|. The read data is sent in the stream |stream_id|. + * The implementation of this function must read at most |length| + * bytes of data from |source| (or possibly other places) and store + * them in |buf| and return number of data stored in |buf|. If EOF is + * reached, set :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag + * in |*data_flags|. + * + * Sometime it is desirable to avoid copying data into |buf| and let + * application to send data directly. To achieve this, set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` to + * |*data_flags| (and possibly other flags, just like when we do + * copy), and return the number of bytes to send without copying data + * into |buf|. The library, seeing + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY`, will invoke + * :type:`nghttp2_send_data_callback`. The application must send + * complete DATA frame in that callback. + * + * If this callback is set by `nghttp2_submit_request2()`, + * `nghttp2_submit_response2()` or `nghttp2_submit_headers()` and + * `nghttp2_submit_data2()` with flag parameter + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` set, and + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` flag is set to + * |*data_flags|, DATA frame will have END_STREAM flag set. Usually, + * this is expected behaviour and all are fine. One exception is send + * trailer fields. You cannot send trailer fields after sending frame + * with END_STREAM set. To avoid this problem, one can set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM` along + * with :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF` to signal the + * library not to set END_STREAM in DATA frame. Then application can + * use `nghttp2_submit_trailer()` to send trailer fields. + * `nghttp2_submit_trailer()` can be called inside this callback. + * + * If the application wants to postpone DATA frames (e.g., + * asynchronous I/O, or reading data blocks for long time), it is + * achieved by returning :enum:`nghttp2_error.NGHTTP2_ERR_DEFERRED` + * without reading any data in this invocation. The library removes + * DATA frame from the outgoing queue temporarily. To move back + * deferred DATA frame to outgoing queue, call + * `nghttp2_session_resume_data()`. + * + * By default, |length| is limited to 16KiB at maximum. If peer + * allows larger frames, application can enlarge transmission buffer + * size. See :type:`nghttp2_data_source_read_length_callback` for + * more details. + * + * If the application just wants to return from + * `nghttp2_session_send()` or `nghttp2_session_mem_send2()` without + * sending anything, return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. + * + * In case of error, there are 2 choices. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will + * close the stream by issuing RST_STREAM with + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. If a different + * error code is desirable, use `nghttp2_submit_rst_stream()` with a + * desired error code and then return + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * Returning :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will + * signal the entire session failure. */ -typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); +typedef nghttp2_ssize (*nghttp2_data_source_read_callback2)( + nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, + uint32_t *data_flags, nghttp2_data_source *source, void *user_data); +#ifndef NGHTTP2_NO_SSIZE_T /** - * @functypedef + * @struct * - * Custom memory allocator to replace realloc(). The |mem_user_data| - * is the mem_user_data member of :type:`nghttp2_mem` structure. + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_data_provider2` instead. + * + * This struct represents the data source and the way to read a chunk + * of data from it. */ -typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); - typedef struct { /** - * An arbitrary user supplied data. This is passed to each - * allocator function. - */ - void *mem_user_data; - /** - * Custom allocator function to replace malloc(). + * The data source. */ - nghttp2_malloc malloc; + nghttp2_data_source source; /** - * Custom allocator function to replace free(). + * The callback function to read a chunk of data from the |source|. */ - nghttp2_free free; + nghttp2_data_source_read_callback read_callback; +} nghttp2_data_provider; + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @struct + * + * This struct represents the data source and the way to read a chunk + * of data from it. + */ +typedef struct { /** - * Custom allocator function to replace calloc(). + * The data source. */ - nghttp2_calloc calloc; + nghttp2_data_source source; /** - * Custom allocator function to replace realloc(). + * The callback function to read a chunk of data from the |source|. */ - nghttp2_realloc realloc; -} nghttp2_mem; - + nghttp2_data_source_read_callback2 read_callback; +} nghttp2_data_provider2; /** * @struct @@ -939,6 +1156,12 @@ typedef enum { /** * @struct * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The structure to specify stream dependency. */ typedef struct { @@ -973,6 +1196,12 @@ typedef struct { */ size_t padlen; /** + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The priority specification */ nghttp2_priority_spec pri_spec; @@ -993,6 +1222,12 @@ typedef struct { /** * @struct * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * * The PRIORITY frame. It has the following members: */ typedef struct { @@ -1236,184 +1471,4890 @@ typedef union { nghttp2_extension ext; } nghttp2_frame; -/* HPACK API */ - -struct nghttp2_hd_deflater; - +#ifndef NGHTTP2_NO_SSIZE_T /** - * @struct + * @functypedef * - * HPACK deflater object. + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_send_callback2` instead. + * + * Callback function invoked when |session| wants to send data to the + * remote peer. The implementation of this function must send at most + * |length| bytes of data stored in |data|. The |flags| is currently + * not used and always 0. It must return the number of bytes sent if + * it succeeds. If it cannot send any single byte without blocking, + * it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. For + * other errors, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_send()` to send data to the remote endpoint. If + * the application uses solely `nghttp2_session_mem_send()` instead, + * this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_send_callback()`. + * + * .. note:: + * + * The |length| may be very small. If that is the case, and + * application disables Nagle algorithm (``TCP_NODELAY``), then just + * writing |data| to the network stack leads to very small packet, + * and it is very inefficient. An application should be responsible + * to buffer up small chunks of data as necessary to avoid this + * situation. */ -typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; +typedef ssize_t (*nghttp2_send_callback)(nghttp2_session *session, + const uint8_t *data, size_t length, + int flags, void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ /** - * @function - * - * Initializes |*deflater_ptr| for deflating name/values pairs. - * - * The |max_deflate_dynamic_table_size| is the upper bound of header - * table size the deflater will use. + * @functypedef * - * If this function fails, |*deflater_ptr| is left untouched. + * Callback function invoked when |session| wants to send data to the + * remote peer. The implementation of this function must send at most + * |length| bytes of data stored in |data|. The |flags| is currently + * not used and always 0. It must return the number of bytes sent if + * it succeeds. If it cannot send any single byte without blocking, + * it must return :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. For + * other errors, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_send()` to send data to the remote endpoint. If + * the application uses solely `nghttp2_session_mem_send2()` instead, + * this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_send_callback2()`. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: + * .. note:: * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. + * The |length| may be very small. If that is the case, and + * application disables Nagle algorithm (``TCP_NODELAY``), then just + * writing |data| to the network stack leads to very small packet, + * and it is very inefficient. An application should be responsible + * to buffer up small chunks of data as necessary to avoid this + * situation. */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, - size_t max_deflate_dynamic_table_size); +typedef nghttp2_ssize (*nghttp2_send_callback2)(nghttp2_session *session, + const uint8_t *data, + size_t length, int flags, + void *user_data); /** - * @function + * @functypedef * - * Like `nghttp2_hd_deflate_new()`, but with additional custom memory - * allocator specified in the |mem|. + * Callback function invoked when + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in + * :type:`nghttp2_data_source_read_callback` to send complete DATA + * frame. * - * The |mem| can be ``NULL`` and the call is equivalent to - * `nghttp2_hd_deflate_new()`. + * The |frame| is a DATA frame to send. The |framehd| is the + * serialized frame header (9 bytes). The |length| is the length of + * application data to send (this does not include padding). The + * |source| is the same pointer passed to + * :type:`nghttp2_data_source_read_callback`. * - * This function does not take ownership |mem|. The application is - * responsible for freeing |mem|. + * The application first must send frame header |framehd| of length 9 + * bytes. If ``frame->data.padlen > 0``, send 1 byte of value + * ``frame->data.padlen - 1``. Then send exactly |length| bytes of + * application data. Finally, if ``frame->data.padlen > 1``, send + * ``frame->data.padlen - 1`` bytes of zero as padding. + * + * The application has to send complete DATA frame in this callback. + * If all data were written successfully, return 0. + * + * If it cannot send any data at all, just return + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`; the library will call + * this callback with the same parameters later (It is recommended to + * send complete DATA frame at once in this function to deal with + * error; if partial frame data has already sent, it is impossible to + * send another data in that state, and all we can do is tear down + * connection). When data is fully processed, but application wants + * to make `nghttp2_session_mem_send2()` or `nghttp2_session_send()` + * return immediately without processing next frames, return + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE`. If application decided to + * reset this stream, return + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`, then + * the library will send RST_STREAM with INTERNAL_ERROR as error code. + * The application can also return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which will + * result in connection closure. Returning any other value is treated + * as :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. + */ +typedef int (*nghttp2_send_data_callback)(nghttp2_session *session, + nghttp2_frame *frame, + const uint8_t *framehd, size_t length, + nghttp2_data_source *source, + void *user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @functypedef * - * The library code does not refer to |mem| pointer after this - * function returns, so the application can safely free it. + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_recv_callback2` instead. + * + * Callback function invoked when |session| wants to receive data from + * the remote peer. The implementation of this function must read at + * most |length| bytes of data and store it in |buf|. The |flags| is + * currently not used and always 0. It must return the number of + * bytes written in |buf| if it succeeds. If it cannot read any + * single byte without blocking, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF + * before it reads any single byte, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`. For other errors, it must + * return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * Returning 0 is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. The |user_data| + * pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_recv()` to receive data from the remote endpoint. + * If the application uses solely `nghttp2_session_mem_recv()` + * instead, this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_recv_callback()`. */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, - size_t max_deflate_dynamic_table_size, - nghttp2_mem *mem); +typedef ssize_t (*nghttp2_recv_callback)(nghttp2_session *session, uint8_t *buf, + size_t length, int flags, + void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ /** - * @function + * @functypedef * - * Deallocates any resources allocated for |deflater|. + * Callback function invoked when |session| wants to receive data from + * the remote peer. The implementation of this function must read at + * most |length| bytes of data and store it in |buf|. The |flags| is + * currently not used and always 0. It must return the number of + * bytes written in |buf| if it succeeds. If it cannot read any + * single byte without blocking, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. If it gets EOF + * before it reads any single byte, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_EOF`. For other errors, it must + * return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * Returning 0 is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. The |user_data| + * pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * This callback is required if the application uses + * `nghttp2_session_recv()` to receive data from the remote endpoint. + * If the application uses solely `nghttp2_session_mem_recv2()` + * instead, this callback function is unnecessary. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_recv_callback2()`. */ -NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); +typedef nghttp2_ssize (*nghttp2_recv_callback2)(nghttp2_session *session, + uint8_t *buf, size_t length, + int flags, void *user_data); /** - * @function + * @functypedef * - * Changes header table size of the |deflater| to - * |settings_max_dynamic_table_size| bytes. This may trigger eviction - * in the dynamic table. + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv2()` when a frame is received. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * - * The |settings_max_dynamic_table_size| should be the value received - * in SETTINGS_HEADER_TABLE_SIZE. + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. The header name/value pairs are emitted via + * :type:`nghttp2_on_header_callback`. * - * The deflater never uses more memory than - * ``max_deflate_dynamic_table_size`` bytes specified in - * `nghttp2_hd_deflate_new()`. Therefore, if - * |settings_max_dynamic_table_size| > - * ``max_deflate_dynamic_table_size``, resulting maximum table size - * becomes ``max_deflate_dynamic_table_size``. + * Only HEADERS and DATA frame can signal the end of incoming data. + * If ``frame->hd.flags & NGHTTP2_FLAG_END_STREAM`` is nonzero, the + * |frame| is the last frame from the remote peer in this stream. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: + * This callback won't be called for CONTINUATION frames. + * HEADERS/PUSH_PROMISE + CONTINUATIONs are treated as single frame. * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_recv_callback()`. */ -NGHTTP2_EXTERN int -nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, - size_t settings_max_dynamic_table_size); +typedef int (*nghttp2_on_frame_recv_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); /** - * @function + * @functypedef * - * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the |buf| of length |buflen|. + * Callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is + * received. The error is indicated by the |lib_error_code|, which is + * one of the values defined in :type:`nghttp2_error`. When this + * callback function is invoked, the library automatically submits + * either RST_STREAM or GOAWAY frame. The |user_data| pointer is the + * third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. + * + * If frame is HEADERS or PUSH_PROMISE, the ``nva`` and ``nvlen`` + * member of their data structure are always ``NULL`` and 0 + * respectively. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_invalid_frame_recv_callback()`. + */ +typedef int (*nghttp2_on_invalid_frame_recv_callback)( + nghttp2_session *session, const nghttp2_frame *frame, int lib_error_code, + void *user_data); + +/** + * @functypedef * - * If |buf| is not large enough to store the deflated header block, - * this function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The - * caller should use `nghttp2_hd_deflate_bound()` to know the upper - * bound of buffer size required to deflate given header name/value - * pairs. + * Callback function invoked when a chunk of data in DATA frame is + * received. The |stream_id| is the stream ID this DATA frame belongs + * to. The |flags| is the flags of DATA frame which this data chunk + * is contained. ``(flags & NGHTTP2_FLAG_END_STREAM) != 0`` does not + * necessarily mean this chunk of data is the last one in the stream. + * You should use :type:`nghttp2_on_frame_recv_callback` to know all + * data frames are received. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * If the application uses `nghttp2_session_mem_recv2()`, it can + * return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make + * `nghttp2_session_mem_recv2()` return without processing further + * input bytes. The memory by pointed by the |data| is retained until + * `nghttp2_session_mem_recv2()` or `nghttp2_session_recv()` is + * called. The application must retain the input bytes which was used + * to produce the |data| parameter, because it may refer to the memory + * region included in the input bytes. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error, and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_data_chunk_recv_callback()`. + */ +typedef int (*nghttp2_on_data_chunk_recv_callback)(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef * - * Once this function fails, subsequent call of this function always - * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. + * Callback function invoked just before the non-DATA frame |frame| is + * sent. The |user_data| pointer is the third argument passed in to + * the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * The implementation of this function must return 0 if it succeeds. + * It can also return :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` to + * cancel the transmission of the given frame. + * + * If there is a fatal error while executing this callback, the + * implementation should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, which makes + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * If the other value is returned, it is treated as if + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. + * But the implementation should not rely on this since the library + * may define new return value to extend its capability. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_before_frame_send_callback()`. + */ +typedef int (*nghttp2_before_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); + +/** + * @functypedef * - * After this function returns, it is safe to delete the |nva|. + * Callback function invoked after the frame |frame| is sent. The + * |user_data| pointer is the third argument passed in to the call to + * `nghttp2_session_client_new()` or `nghttp2_session_server_new()`. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` - * Deflation process has failed. - * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` - * The provided |buflen| size is too small to hold the output. + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_send_callback()`. */ -NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, - uint8_t *buf, size_t buflen, - const nghttp2_nv *nva, - size_t nvlen); +typedef int (*nghttp2_on_frame_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); /** - * @function - * - * Deflates the |nva|, which has the |nvlen| name/value pairs, into - * the |veclen| size of buf vector |vec|. The each size of buffer - * must be set in len field of :type:`nghttp2_vec`. If and only if - * one chunk is filled up completely, next chunk will be used. If - * |vec| is not large enough to store the deflated header block, this - * function fails with :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller - * should use `nghttp2_hd_deflate_bound()` to know the upper bound of - * buffer size required to deflate given header name/value pairs. + * @functypedef * - * Once this function fails, subsequent call of this function always - * returns :enum:`NGHTTP2_ERR_HEADER_COMP`. + * Callback function invoked after the non-DATA frame |frame| is not + * sent because of the error. The error is indicated by the + * |lib_error_code|, which is one of the values defined in + * :type:`nghttp2_error`. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. * - * After this function returns, it is safe to delete the |nva|. + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: + * `nghttp2_session_get_stream_user_data()` can be used to get + * associated data. * - * :enum:`NGHTTP2_ERR_NOMEM` - * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` - * Deflation process has failed. - * :enum:`NGHTTP2_ERR_INSUFF_BUFSIZE` - * The provided |buflen| size is too small to hold the output. + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_frame_not_send_callback()`. */ -NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, - const nghttp2_vec *vec, - size_t veclen, - const nghttp2_nv *nva, - size_t nvlen); +typedef int (*nghttp2_on_frame_not_send_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + int lib_error_code, + void *user_data); /** - * @function + * @functypedef * - * Returns an upper bound on the compressed size after deflation of - * |nva| of length |nvlen|. + * Callback function invoked when the stream |stream_id| is closed. + * The reason of closure is indicated by the |error_code|. The + * |error_code| is usually one of :enum:`nghttp2_error_code`, but that + * is not guaranteed. The stream_user_data, which was specified in + * `nghttp2_submit_request2()` or `nghttp2_submit_headers()`, is still + * available in this function. The |user_data| pointer is the third + * argument passed in to the call to `nghttp2_session_client_new()` or + * `nghttp2_session_server_new()`. + * + * This function is also called for a stream in reserved state. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero is returned, it is treated as fatal error and + * `nghttp2_session_recv()`, `nghttp2_session_mem_recv2()`, + * `nghttp2_session_send()`, and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_stream_close_callback()`. */ -NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, - const nghttp2_nv *nva, - size_t nvlen); +typedef int (*nghttp2_on_stream_close_callback)(nghttp2_session *session, + int32_t stream_id, + uint32_t error_code, + void *user_data); /** - * @function + * @functypedef * - * Returns the number of entries that header table of |deflater| - * contains. This is the sum of the number of static table and - * dynamic table, so the return value is at least 61. + * Callback function invoked when the reception of header block in + * HEADERS or PUSH_PROMISE is started. Each header name/value pair + * will be emitted by :type:`nghttp2_on_header_callback`. + * + * The ``frame->hd.flags`` may not have + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_HEADERS` flag set, which + * indicates that one or more CONTINUATION frames are involved. But + * the application does not need to care about that because the header + * name/value pairs are emitted transparently regardless of + * CONTINUATION frames. + * + * The server applications probably create an object to store + * information about new stream if ``frame->hd.type == + * NGHTTP2_HEADERS`` and ``frame->headers.cat == + * NGHTTP2_HCAT_REQUEST``. If |session| is configured as server side, + * ``frame->headers.cat`` is either ``NGHTTP2_HCAT_REQUEST`` + * containing request headers or ``NGHTTP2_HCAT_HEADERS`` containing + * trailer fields and never get PUSH_PROMISE in this callback. + * + * For the client applications, ``frame->hd.type`` is either + * ``NGHTTP2_HEADERS`` or ``NGHTTP2_PUSH_PROMISE``. In case of + * ``NGHTTP2_HEADERS``, ``frame->headers.cat == + * NGHTTP2_HCAT_RESPONSE`` means that it is the first response + * headers, but it may be non-final response which is indicated by 1xx + * status code. In this case, there may be zero or more HEADERS frame + * with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` which has + * non-final response code and finally client gets exactly one HEADERS + * frame with ``frame->headers.cat == NGHTTP2_HCAT_HEADERS`` + * containing final response headers (non-1xx status code). The + * trailer fields also has ``frame->headers.cat == + * NGHTTP2_HCAT_HEADERS`` which does not contain any status code. + * + * Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will + * close the stream (promised stream if frame is PUSH_PROMISE) by + * issuing RST_STREAM with + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * Again, use ``frame->push_promise.promised_stream_id`` as stream_id + * parameter in `nghttp2_submit_rst_stream()` if frame is + * PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It can return + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` to + * reset the stream (promised stream if frame is PUSH_PROMISE). For + * critical errors, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * value is returned, it is treated as if + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned. If + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_mem_recv2()` function will immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_headers_callback()`. */ -NGHTTP2_EXTERN -size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater); +typedef int (*nghttp2_on_begin_headers_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + void *user_data); /** - * @function + * @functypedef * - * Returns the table entry denoted by |idx| from header table of - * |deflater|. The |idx| is 1-based, and idx=1 returns first entry of - * static table. idx=62 returns first entry of dynamic table if it - * exists. Specifying idx=0 is error, and this function returns NULL. - * If |idx| is strictly greater than the number of entries the tables + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| of length |namelen| is header name. + * The |value| of length |valuelen| is header value. The |flags| is + * bitwise OR of one or more of :type:`nghttp2_nv_flag`. + * + * If :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_INDEX` is set in + * |flags|, the receiver must not index this name/value pair when + * forwarding it to the next hop. More specifically, "Literal Header + * Field never Indexed" representation must be used in HPACK encoding. + * + * When this callback is invoked, ``frame->hd.type`` is either + * :enum:`nghttp2_frame_type.NGHTTP2_HEADERS` or + * :enum:`nghttp2_frame_type.NGHTTP2_PUSH_PROMISE`. After all header + * name/value pairs are processed with this callback, and no error has + * been detected, :type:`nghttp2_on_frame_recv_callback` will be + * invoked. If there is an error in decompression, + * :type:`nghttp2_on_frame_recv_callback` for the |frame| will not be + * invoked. + * + * Both |name| and |value| are guaranteed to be NULL-terminated. The + * |namelen| and |valuelen| do not include terminal NULL. If + * `nghttp2_option_set_no_http_messaging()` is used with nonzero + * value, NULL character may be included in |name| or |value| before + * terminating NULL. + * + * Please note that unless `nghttp2_option_set_no_http_messaging()` is + * used, nghttp2 library does perform validation against the |name| + * and the |value| using `nghttp2_check_header_name()` and + * `nghttp2_check_header_value()`. In addition to this, nghttp2 + * performs validation based on HTTP Messaging rule, which is briefly + * explained in :ref:`http-messaging` section. + * + * If the application uses `nghttp2_session_mem_recv2()`, it can + * return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` to make + * `nghttp2_session_mem_recv2()` return without processing further + * input bytes. The memory pointed by |frame|, |name| and |value| + * parameters are retained until `nghttp2_session_mem_recv2()` or + * `nghttp2_session_recv()` is called. The application must retain + * the input bytes which was used to produce these parameters, because + * it may refer to the memory region included in the input bytes. + * + * Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE` will + * close the stream (promised stream if frame is PUSH_PROMISE) by + * issuing RST_STREAM with + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. In this case, + * :type:`nghttp2_on_header_callback` and + * :type:`nghttp2_on_frame_recv_callback` will not be invoked. If a + * different error code is desirable, use + * `nghttp2_submit_rst_stream()` with a desired error code and then + * return :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * Again, use ``frame->push_promise.promised_stream_id`` as stream_id + * parameter in `nghttp2_submit_rst_stream()` if frame is + * PUSH_PROMISE. + * + * The implementation of this function must return 0 if it succeeds. + * It may return :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` or + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. For + * other critical failures, it must return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * nonzero value is returned, it is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` is returned, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_header_callback()`. + * + * .. warning:: + * + * Application should properly limit the total buffer size to store + * incoming header fields. Without it, peer may send large number + * of header fields or large header fields to cause out of memory in + * local endpoint. Due to how HPACK works, peer can do this + * effectively without using much memory on their own. + */ +typedef int (*nghttp2_on_header_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + const uint8_t *name, size_t namelen, + const uint8_t *value, size_t valuelen, + uint8_t flags, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a header name/value pair is received + * for the |frame|. The |name| is header name. The |value| is header + * value. The |flags| is bitwise OR of one or more of + * :type:`nghttp2_nv_flag`. + * + * This callback behaves like :type:`nghttp2_on_header_callback`, + * except that |name| and |value| are stored in reference counted + * buffer. If application wishes to keep these references without + * copying them, use `nghttp2_rcbuf_incref()` to increment their + * reference count. It is the application's responsibility to call + * `nghttp2_rcbuf_decref()` if they called `nghttp2_rcbuf_incref()` so + * as not to leak memory. If the |session| is created by + * `nghttp2_session_server_new3()` or `nghttp2_session_client_new3()`, + * the function to free memory is the one belongs to the mem + * parameter. As long as this free function alives, |name| and + * |value| can live after |session| was destroyed. + */ +typedef int (*nghttp2_on_header_callback2)(nghttp2_session *session, + const nghttp2_frame *frame, + nghttp2_rcbuf *name, + nghttp2_rcbuf *value, uint8_t flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback`. The difference is that this + * callback is only invoked when a invalid header name/value pair is + * received which is treated as stream error if this callback is not + * set. Only invalid regular header field are passed to this + * callback. In other words, invalid pseudo header field is not + * passed to this callback. Also header fields which includes upper + * cased latter are also treated as error without passing them to this + * callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see + * `nghttp2_option_set_no_http_messaging()`). + * + * With this callback, application inspects the incoming invalid + * field, and it also can reset stream from this callback by returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By + * default, the error code is + * :enum:`nghttp2_error_code.NGHTTP2_PROTOCOL_ERROR`. To change the + * error code, call `nghttp2_submit_rst_stream()` with the error code + * of choice in addition to returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + * + * If 0 is returned, the header field is ignored, and the stream is + * not reset. + */ +typedef int (*nghttp2_on_invalid_header_callback)( + nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, + size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a invalid header name/value pair is + * received for the |frame|. + * + * The parameter and behaviour are similar to + * :type:`nghttp2_on_header_callback2`. The difference is that this + * callback is only invoked when a invalid header name/value pair is + * received which is silently ignored if this callback is not set. + * Only invalid regular header field are passed to this callback. In + * other words, invalid pseudo header field is not passed to this + * callback. Also header fields which includes upper cased latter are + * also treated as error without passing them to this callback. + * + * This callback is only considered if HTTP messaging validation is + * turned on (which is on by default, see + * `nghttp2_option_set_no_http_messaging()`). + * + * With this callback, application inspects the incoming invalid + * field, and it also can reset stream from this callback by returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. By + * default, the error code is + * :enum:`nghttp2_error_code.NGHTTP2_INTERNAL_ERROR`. To change the + * error code, call `nghttp2_submit_rst_stream()` with the error code + * of choice in addition to returning + * :enum:`nghttp2_error.NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE`. + */ +typedef int (*nghttp2_on_invalid_header_callback2)( + nghttp2_session *session, const nghttp2_frame *frame, nghttp2_rcbuf *name, + nghttp2_rcbuf *value, uint8_t flags, void *user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @functypedef + * + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_select_padding_callback2` + * instead. + * + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the + * |frame|. The application must choose the total length of payload + * including padded bytes in range [frame->hd.length, max_payloadlen], + * inclusive. Choosing number not in this range will be treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning + * ``frame->hd.length`` means no padding is added. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_select_padding_callback()`. + */ +typedef ssize_t (*nghttp2_select_padding_callback)(nghttp2_session *session, + const nghttp2_frame *frame, + size_t max_payloadlen, + void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @functypedef + * + * Callback function invoked when the library asks application how + * many padding bytes are required for the transmission of the + * |frame|. The application must choose the total length of payload + * including padded bytes in range [frame->hd.length, max_payloadlen], + * inclusive. Choosing number not in this range will be treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Returning + * ``frame->hd.length`` means no padding is added. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will make + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_select_padding_callback2()`. + */ +typedef nghttp2_ssize (*nghttp2_select_padding_callback2)( + nghttp2_session *session, const nghttp2_frame *frame, size_t max_payloadlen, + void *user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @functypedef + * + * .. warning:: + * + * Deprecated. Use + * :type:`nghttp2_data_source_read_length_callback2` instead. + * + * Callback function invoked when library wants to get max length of + * data to send data to the remote peer. The implementation of this + * function should return a value in the following range. [1, + * min(|session_remote_window_size|, |stream_remote_window_size|, + * |remote_max_frame_size|)]. If a value greater than this range is + * returned than the max allow value will be used. Returning a value + * smaller than this range is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |frame_type| is provided for future extensibility and identifies + * the type of frame (see :type:`nghttp2_frame_type`) for which to get + * the length for. Currently supported frame types are: + * :enum:`nghttp2_frame_type.NGHTTP2_DATA`. + * + * This callback can be used to control the length in bytes for which + * :type:`nghttp2_data_source_read_callback` is allowed to send to the + * remote endpoint. This callback is optional. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the + * entire session failure. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_data_source_read_length_callback()`. + */ +typedef ssize_t (*nghttp2_data_source_read_length_callback)( + nghttp2_session *session, uint8_t frame_type, int32_t stream_id, + int32_t session_remote_window_size, int32_t stream_remote_window_size, + uint32_t remote_max_frame_size, void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @functypedef + * + * Callback function invoked when library wants to get max length of + * data to send data to the remote peer. The implementation of this + * function should return a value in the following range. [1, + * min(|session_remote_window_size|, |stream_remote_window_size|, + * |remote_max_frame_size|)]. If a value greater than this range is + * returned than the max allow value will be used. Returning a value + * smaller than this range is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. The + * |frame_type| is provided for future extensibility and identifies + * the type of frame (see :type:`nghttp2_frame_type`) for which to get + * the length for. Currently supported frame types are: + * :enum:`nghttp2_frame_type.NGHTTP2_DATA`. + * + * This callback can be used to control the length in bytes for which + * :type:`nghttp2_data_source_read_callback` is allowed to send to the + * remote endpoint. This callback is optional. Returning + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` will signal the + * entire session failure. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_data_source_read_length_callback2()`. + */ +typedef nghttp2_ssize (*nghttp2_data_source_read_length_callback2)( + nghttp2_session *session, uint8_t frame_type, int32_t stream_id, + int32_t session_remote_window_size, int32_t stream_remote_window_size, + uint32_t remote_max_frame_size, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when a frame header is received. The + * |hd| points to received frame header. + * + * Unlike :type:`nghttp2_on_frame_recv_callback`, this callback will + * also be called when frame header of CONTINUATION frame is received. + * + * If both :type:`nghttp2_on_begin_frame_callback` and + * :type:`nghttp2_on_begin_headers_callback` are set and HEADERS or + * PUSH_PROMISE is received, :type:`nghttp2_on_begin_frame_callback` + * will be called first. + * + * The implementation of this function must return 0 if it succeeds. + * If nonzero value is returned, it is treated as fatal error and + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * + * To set this callback to :type:`nghttp2_session_callbacks`, use + * `nghttp2_session_callbacks_set_on_begin_frame_callback()`. + */ +typedef int (*nghttp2_on_begin_frame_callback)(nghttp2_session *session, + const nghttp2_frame_hd *hd, + void *user_data); + +/** + * @functypedef + * + * Callback function invoked when chunk of extension frame payload is + * received. The |hd| points to frame header. The received + * chunk is |data| of length |len|. + * + * The implementation of this function must return 0 if it succeeds. + * + * To abort processing this extension frame, return + * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. + * + * If fatal error occurred, application should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * values are returned, currently they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp2_on_extension_chunk_recv_callback)( + nghttp2_session *session, const nghttp2_frame_hd *hd, const uint8_t *data, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library asks the application to + * unpack extension payload from its wire format. The extension + * payload has been passed to the application using + * :type:`nghttp2_on_extension_chunk_recv_callback`. The frame header + * is already unpacked by the library and provided as |hd|. + * + * To receive extension frames, the application must tell desired + * extension frame type to the library using + * `nghttp2_option_set_user_recv_extension_type()`. + * + * The implementation of this function may store the pointer to the + * created object as a result of unpacking in |*payload|, and returns + * 0. The pointer stored in |*payload| is opaque to the library, and + * the library does not own its pointer. |*payload| is initialized as + * ``NULL``. The |*payload| is available as ``frame->ext.payload`` in + * :type:`nghttp2_on_frame_recv_callback`. Therefore if application + * can free that memory inside :type:`nghttp2_on_frame_recv_callback` + * callback. Of course, application has a liberty not to use + * |*payload|, and do its own mechanism to process extension frames. + * + * To abort processing this extension frame, return + * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`. + * + * If fatal error occurred, application should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_recv()` and `nghttp2_session_mem_recv2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * values are returned, currently they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp2_unpack_extension_callback)(nghttp2_session *session, + void **payload, + const nghttp2_frame_hd *hd, + void *user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @functypedef + * + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_pack_extension_callback2` + * instead. + * + * Callback function invoked when library asks the application to pack + * extension payload in its wire format. The frame header will be + * packed by library. Application must pack payload only. + * ``frame->ext.payload`` is the object passed to + * `nghttp2_submit_extension()` as payload parameter. Application + * must pack extension payload to the |buf| of its capacity |len| + * bytes. The |len| is at least 16KiB. + * + * The implementation of this function should return the number of + * bytes written into |buf| when it succeeds. + * + * To abort processing this extension frame, return + * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and + * :type:`nghttp2_on_frame_not_send_callback` will be invoked. + * + * If fatal error occurred, application should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_send()` and `nghttp2_session_mem_send()` functions + * immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * values are returned, currently they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the return + * value is strictly larger than |len|, it is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef ssize_t (*nghttp2_pack_extension_callback)(nghttp2_session *session, + uint8_t *buf, size_t len, + const nghttp2_frame *frame, + void *user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @functypedef + * + * Callback function invoked when library asks the application to pack + * extension payload in its wire format. The frame header will be + * packed by library. Application must pack payload only. + * ``frame->ext.payload`` is the object passed to + * `nghttp2_submit_extension()` as payload parameter. Application + * must pack extension payload to the |buf| of its capacity |len| + * bytes. The |len| is at least 16KiB. + * + * The implementation of this function should return the number of + * bytes written into |buf| when it succeeds. + * + * To abort processing this extension frame, return + * :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL`, and + * :type:`nghttp2_on_frame_not_send_callback` will be invoked. + * + * If fatal error occurred, application should return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. In this case, + * `nghttp2_session_send()` and `nghttp2_session_mem_send2()` + * functions immediately return + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the other + * values are returned, currently they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. If the return + * value is strictly larger than |len|, it is treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + */ +typedef nghttp2_ssize (*nghttp2_pack_extension_callback2)( + nghttp2_session *session, uint8_t *buf, size_t len, + const nghttp2_frame *frame, void *user_data); + +/** + * @functypedef + * + * .. warning:: + * + * Deprecated. Use :type:`nghttp2_error_callback2` instead. + * + * Callback function invoked when library provides the error message + * intended for human consumption. This callback is solely for + * debugging purpose. The |msg| is typically NULL-terminated string + * of length |len|. |len| does not include the sentinel NULL + * character. + * + * The format of error message may change between nghttp2 library + * versions. The application should not depend on the particular + * format. + * + * Normally, application should return 0 from this callback. If fatal + * error occurred while doing something in this callback, application + * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * In this case, library will return immediately with return value + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if + * nonzero value is returned from this callback, they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application + * should not rely on this details. + */ +typedef int (*nghttp2_error_callback)(nghttp2_session *session, const char *msg, + size_t len, void *user_data); + +/** + * @functypedef + * + * Callback function invoked when library provides the error code, and + * message. This callback is solely for debugging purpose. + * |lib_error_code| is one of error code defined in + * :enum:`nghttp2_error`. The |msg| is typically NULL-terminated + * string of length |len|, and intended for human consumption. |len| + * does not include the sentinel NULL character. + * + * The format of error message may change between nghttp2 library + * versions. The application should not depend on the particular + * format. + * + * Normally, application should return 0 from this callback. If fatal + * error occurred while doing something in this callback, application + * should return :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. + * In this case, library will return immediately with return value + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`. Currently, if + * nonzero value is returned from this callback, they are treated as + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE`, but application + * should not rely on this details. + */ +typedef int (*nghttp2_error_callback2)(nghttp2_session *session, + int lib_error_code, const char *msg, + size_t len, void *user_data); + +struct nghttp2_session_callbacks; + +/** + * @struct + * + * Callback functions for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_session_callbacks nghttp2_session_callbacks; + +/** + * @function + * + * Initializes |*callbacks_ptr| with NULL values. + * + * The initialized object can be used when initializing multiple + * :type:`nghttp2_session` objects. + * + * When the application finished using this object, it can use + * `nghttp2_session_callbacks_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_callbacks_new(nghttp2_session_callbacks **callbacks_ptr); + +/** + * @function + * + * Frees any resources allocated for |callbacks|. If |callbacks| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_del(nghttp2_session_callbacks *callbacks); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_session_callbacks_set_send_callback2()` + * with :type:`nghttp2_send_callback2` instead. + * + * Sets callback function invoked when a session wants to send data to + * the remote peer. This callback is not necessary if the application + * uses solely `nghttp2_session_mem_send()` to serialize data to + * transmit. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback( + nghttp2_session_callbacks *cbs, nghttp2_send_callback send_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function invoked when a session wants to send data to + * the remote peer. This callback is not necessary if the application + * uses solely `nghttp2_session_mem_send2()` to serialize data to + * transmit. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_callback2( + nghttp2_session_callbacks *cbs, nghttp2_send_callback2 send_callback); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_session_callbacks_set_recv_callback2()` + * with :type:`nghttp2_recv_callback2` instead. + * + * Sets callback function invoked when the a session wants to receive + * data from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv()` to process + * received data. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback recv_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function invoked when the a session wants to receive + * data from the remote peer. This callback is not necessary if the + * application uses solely `nghttp2_session_mem_recv2()` to process + * received data. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_recv_callback2( + nghttp2_session_callbacks *cbs, nghttp2_recv_callback2 recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv2()` when a frame is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_recv_callback on_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked by `nghttp2_session_recv()` and + * `nghttp2_session_mem_recv2()` when an invalid non-DATA frame is + * received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_invalid_frame_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_frame_recv_callback on_invalid_frame_recv_callback); + +/** + * @function + * + * Sets callback function invoked when a chunk of data in DATA frame + * is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_data_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_data_chunk_recv_callback on_data_chunk_recv_callback); + +/** + * @function + * + * Sets callback function invoked before a non-DATA frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_before_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_before_frame_send_callback before_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked after a frame is sent. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_send_callback on_frame_send_callback); + +/** + * @function + * + * Sets callback function invoked when a non-DATA frame is not sent + * because of an error. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_frame_not_send_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_frame_not_send_callback on_frame_not_send_callback); + +/** + * @function + * + * Sets callback function invoked when the stream is closed. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_stream_close_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_stream_close_callback on_stream_close_callback); + +/** + * @function + * + * Sets callback function invoked when the reception of header block + * in HEADERS or PUSH_PROMISE is started. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_headers_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_headers_callback on_begin_headers_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. If both + * `nghttp2_session_callbacks_set_on_header_callback()` and + * `nghttp2_session_callbacks_set_on_header_callback2()` are used to + * set callbacks, the latter has the precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback on_header_callback); + +/** + * @function + * + * Sets callback function invoked when a header name/value pair is + * received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_header_callback2 on_header_callback2); + +/** + * @function + * + * Sets callback function invoked when a invalid header name/value + * pair is received. If both + * `nghttp2_session_callbacks_set_on_invalid_header_callback()` and + * `nghttp2_session_callbacks_set_on_invalid_header_callback2()` are + * used to set callbacks, the latter takes the precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback on_invalid_header_callback); + +/** + * @function + * + * Sets callback function invoked when a invalid header name/value + * pair is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_invalid_header_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_on_invalid_header_callback2 on_invalid_header_callback2); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use + * `nghttp2_session_callbacks_set_select_padding_callback2()` with + * :type:`nghttp2_select_padding_callback2` instead. + * + * Sets callback function invoked when the library asks application + * how many padding bytes are required for the transmission of the + * given frame. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback select_padding_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function invoked when the library asks application + * how many padding bytes are required for the transmission of the + * given frame. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_select_padding_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_select_padding_callback2 select_padding_callback); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use + * `nghttp2_session_callbacks_set_data_source_read_length_callback2()` + * with :type:`nghttp2_data_source_read_length_callback2` instead. + * + * Sets callback function determine the length allowed in + * :type:`nghttp2_data_source_read_callback`. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_data_source_read_length_callback( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback data_source_read_length_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function determine the length allowed in + * :type:`nghttp2_data_source_read_callback2`. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_data_source_read_length_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_data_source_read_length_callback2 data_source_read_length_callback); + +/** + * @function + * + * Sets callback function invoked when a frame header is received. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_on_begin_frame_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_begin_frame_callback on_begin_frame_callback); + +/** + * @function + * + * Sets callback function invoked when + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_COPY` is used in + * :type:`nghttp2_data_source_read_callback2` to avoid data copy. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_send_data_callback( + nghttp2_session_callbacks *cbs, + nghttp2_send_data_callback send_data_callback); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use + * `nghttp2_session_callbacks_set_pack_extension_callback2()` with + * :type:`nghttp2_pack_extension_callback2` instead. + * + * Sets callback function invoked when the library asks the + * application to pack extension frame payload in wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_pack_extension_callback pack_extension_callback); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Sets callback function invoked when the library asks the + * application to pack extension frame payload in wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_pack_extension_callback2( + nghttp2_session_callbacks *cbs, + nghttp2_pack_extension_callback2 pack_extension_callback); + +/** + * @function + * + * Sets callback function invoked when the library asks the + * application to unpack extension frame payload from wire format. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_unpack_extension_callback( + nghttp2_session_callbacks *cbs, + nghttp2_unpack_extension_callback unpack_extension_callback); + +/** + * @function + * + * Sets callback function invoked when chunk of extension frame + * payload is received. + */ +NGHTTP2_EXTERN void +nghttp2_session_callbacks_set_on_extension_chunk_recv_callback( + nghttp2_session_callbacks *cbs, + nghttp2_on_extension_chunk_recv_callback on_extension_chunk_recv_callback); + +/** + * @function + * + * .. warning:: + * + * Deprecated. Use + * `nghttp2_session_callbacks_set_error_callback2()` with + * :type:`nghttp2_error_callback2` instead. + * + * Sets callback function invoked when library tells error message to + * the application. + * + * If both :type:`nghttp2_error_callback` and + * :type:`nghttp2_error_callback2` are set, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback( + nghttp2_session_callbacks *cbs, nghttp2_error_callback error_callback); + +/** + * @function + * + * Sets callback function invoked when library tells error code, and + * message to the application. + * + * If both :type:`nghttp2_error_callback` and + * :type:`nghttp2_error_callback2` are set, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void nghttp2_session_callbacks_set_error_callback2( + nghttp2_session_callbacks *cbs, nghttp2_error_callback2 error_callback2); + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void (*nghttp2_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp2_mem` structure. + */ +typedef void *(*nghttp2_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * Custom memory allocator functions and user defined pointer. The + * |mem_user_data| member is passed to each allocator function. This + * can be used, for example, to achieve per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void session_new() { + * nghttp2_session *session; + * nghttp2_session_callbacks *callbacks; + * nghttp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * + * nghttp2_session_client_new3(&session, callbacks, NULL, NULL, &mem); + * + * ... + * } + */ +typedef struct { + /** + * An arbitrary user supplied data. This is passed to each + * allocator function. + */ + void *mem_user_data; + /** + * Custom allocator function to replace malloc(). + */ + nghttp2_malloc malloc; + /** + * Custom allocator function to replace free(). + */ + nghttp2_free free; + /** + * Custom allocator function to replace calloc(). + */ + nghttp2_calloc calloc; + /** + * Custom allocator function to replace realloc(). + */ + nghttp2_realloc realloc; +} nghttp2_mem; + +struct nghttp2_option; + +/** + * @struct + * + * Configuration options for :type:`nghttp2_session`. The details of + * this structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_option nghttp2_option; + +/** + * @function + * + * Initializes |*option_ptr| with default values. + * + * When the application finished using this object, it can use + * `nghttp2_option_del()` to free its memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_option_new(nghttp2_option **option_ptr); + +/** + * @function + * + * Frees any resources allocated for |option|. If |option| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_option_del(nghttp2_option *option); + +/** + * @function + * + * This option prevents the library from sending WINDOW_UPDATE for a + * connection automatically. If this option is set to nonzero, the + * library won't send WINDOW_UPDATE for DATA until application calls + * `nghttp2_session_consume()` to indicate the consumed amount of + * data. Don't use `nghttp2_submit_window_update()` for this purpose. + * By default, this option is set to zero. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_auto_window_update(nghttp2_option *option, int val); + +/** + * @function + * + * This option sets the SETTINGS_MAX_CONCURRENT_STREAMS value of + * remote endpoint as if it is received in SETTINGS frame. Without + * specifying this option, the maximum number of outgoing concurrent + * streams is initially limited to 100 to avoid issues when the local + * endpoint submits lots of requests before receiving initial SETTINGS + * frame from the remote endpoint, since sending them at once to the + * remote endpoint could lead to rejection of some of the requests. + * This value will be overwritten when the local endpoint receives + * initial SETTINGS frame from the remote endpoint, either to the + * value advertised in SETTINGS_MAX_CONCURRENT_STREAMS or to the + * default value (unlimited) if none was advertised. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_peer_max_concurrent_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * By default, nghttp2 library, if configured as server, requires + * first 24 bytes of client magic byte string (MAGIC). In most cases, + * this will simplify the implementation of server. But sometimes + * server may want to detect the application protocol based on first + * few bytes on clear text communication. + * + * If this option is used with nonzero |val|, nghttp2 library does not + * handle MAGIC. It still checks following SETTINGS frame. This + * means that applications should deal with MAGIC by themselves. + * + * If this option is not used or used with zero value, if MAGIC does + * not match :macro:`NGHTTP2_CLIENT_MAGIC`, `nghttp2_session_recv()` + * and `nghttp2_session_mem_recv2()` will return error + * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC`, which is fatal + * error. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_recv_client_magic(nghttp2_option *option, int val); + +/** + * @function + * + * By default, nghttp2 library enforces subset of HTTP Messaging rules + * described in `HTTP/2 specification, section 8 + * `_. See + * :ref:`http-messaging` section for details. For those applications + * who use nghttp2 library as non-HTTP use, give nonzero to |val| to + * disable this enforcement. Please note that disabling this feature + * does not change the fundamental client and server model of HTTP. + * That is, even if the validation is disabled, only client can send + * requests. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_http_messaging(nghttp2_option *option, + int val); + +/** + * @function + * + * RFC 7540 does not enforce any limit on the number of incoming + * reserved streams (in RFC 7540 terms, streams in reserved (remote) + * state). This only affects client side, since only server can push + * streams. Malicious server can push arbitrary number of streams, + * and make client's memory exhausted. This option can set the + * maximum number of such incoming streams to avoid possible memory + * exhaustion. If this option is set, and pushed streams are + * automatically closed on reception, without calling user provided + * callback, if they exceed the given limit. The default value is + * 200. If session is configured as server side, this option has no + * effect. Server can control the number of streams to push. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_reserved_remote_streams(nghttp2_option *option, + uint32_t val); + +/** + * @function + * + * Sets extension frame type the application is willing to handle with + * user defined callbacks (see + * :type:`nghttp2_on_extension_chunk_recv_callback` and + * :type:`nghttp2_unpack_extension_callback`). The |type| is + * extension frame type, and must be strictly greater than 0x9. + * Otherwise, this function does nothing. The application can call + * this function multiple times to set more than one frame type to + * receive. The application does not have to call this function if it + * just sends extension frames. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_user_recv_extension_type(nghttp2_option *option, + uint8_t type); + +/** + * @function + * + * Sets extension frame type the application is willing to receive + * using builtin handler. The |type| is the extension frame type to + * receive, and must be strictly greater than 0x9. Otherwise, this + * function does nothing. The application can call this function + * multiple times to set more than one frame type to receive. The + * application does not have to call this function if it just sends + * extension frames. + * + * If same frame type is passed to both + * `nghttp2_option_set_builtin_recv_extension_type()` and + * `nghttp2_option_set_user_recv_extension_type()`, the latter takes + * precedence. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_builtin_recv_extension_type(nghttp2_option *option, + uint8_t type); + +/** + * @function + * + * This option prevents the library from sending PING frame with ACK + * flag set automatically when PING frame without ACK flag set is + * received. If this option is set to nonzero, the library won't send + * PING frame with ACK flag set in the response for incoming PING + * frame. The application can send PING frame with ACK flag set using + * `nghttp2_submit_ping()` with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` + * as flags parameter. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_auto_ping_ack(nghttp2_option *option, + int val); + +/** + * @function + * + * This option sets the maximum length of header block (a set of + * header fields per one HEADERS frame) to send. The length of a + * given set of header fields is calculated using + * `nghttp2_hd_deflate_bound()`. The default value is 64KiB. If + * application attempts to send header fields larger than this limit, + * the transmission of the frame fails with error code + * :enum:`nghttp2_error.NGHTTP2_ERR_FRAME_SIZE_ERROR`. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_send_header_block_length(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option sets the maximum dynamic table size for deflating + * header fields. The default value is 4KiB. In HTTP/2, receiver of + * deflated header block can specify maximum dynamic table size. The + * actual maximum size is the minimum of the size receiver specified + * and this option value. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option prevents the library from retaining closed streams to + * maintain the priority tree. If this option is set to nonzero, + * applications can discard closed stream completely to save memory. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is submitted via `nghttp2_submit_settings()`, any + * closed streams are not retained regardless of this option. + */ +NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option, + int val); + +/** + * @function + * + * This function sets the maximum number of outgoing SETTINGS ACK and + * PING ACK frames retained in :type:`nghttp2_session` object. If + * more than those frames are retained, the peer is considered to be + * misbehaving and session will be closed. The default value is 1000. + */ +NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This function sets the maximum number of SETTINGS entries per + * SETTINGS frame that will be accepted. If more than those entries + * are received, the peer is considered to be misbehaving and session + * will be closed. The default value is 32. + */ +NGHTTP2_EXTERN void nghttp2_option_set_max_settings(nghttp2_option *option, + size_t val); + +/** + * @function + * + * This option, if set to nonzero, allows server to fallback to + * :rfc:`7540` priorities if SETTINGS_NO_RFC7540_PRIORITIES was not + * received from client, and server submitted + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * = 1 via `nghttp2_submit_settings()`. Most of the advanced + * functionality for RFC 7540 priorities are still disabled. This + * fallback only enables the minimal feature set of RFC 7540 + * priorities to deal with priority signaling from client. + * + * Client session ignores this option. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_server_fallback_rfc7540_priorities(nghttp2_option *option, + int val); + +/** + * @function + * + * This option, if set to nonzero, turns off RFC 9113 leading and + * trailing white spaces validation against HTTP field value. Some + * important fields, such as HTTP/2 pseudo header fields, are + * validated more strictly and this option does not apply to them. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation( + nghttp2_option *option, int val); + +/** + * @function + * + * This function sets the rate limit for the incoming stream reset + * (RST_STREAM frame). It is server use only. It is a token-bucket + * based rate limiter. |burst| specifies the number of tokens that is + * initially available. The maximum number of tokens is capped to + * this value. |rate| specifies the number of tokens that are + * regenerated per second. An incoming RST_STREAM consumes one token. + * If there is no token available, GOAWAY is sent to tear down the + * connection. |burst| and |rate| default to 1000 and 33 + * respectively. + */ +NGHTTP2_EXTERN void +nghttp2_option_set_stream_reset_rate_limit(nghttp2_option *option, + uint64_t burst, uint64_t rate); + +/** + * @function + * + * This function sets the maximum number of CONTINUATION frames + * following an incoming HEADER frame. If more than those frames are + * received, the remote endpoint is considered to be misbehaving and + * session will be closed. The default value is 8. + */ +NGHTTP2_EXTERN void nghttp2_option_set_max_continuations(nghttp2_option *option, + size_t val); + +/** + * @function + * + * Initializes |*session_ptr| for client use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback2` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Initializes |*session_ptr| for server use. The all members of + * |callbacks| are copied to |*session_ptr|. Therefore |*session_ptr| + * does not store |callbacks|. The |user_data| is an arbitrary user + * supplied data, which will be passed to the callback functions. + * + * The :type:`nghttp2_send_callback2` must be specified. If the + * application code uses `nghttp2_session_recv()`, the + * :type:`nghttp2_recv_callback` must be specified. The other members + * of |callbacks| can be ``NULL``. + * + * If this function fails, |*session_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data); + +/** + * @function + * + * Like `nghttp2_session_client_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_client_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_server_new()`, but with additional options + * specified in the |option|. + * + * The |option| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new()`. + * + * This function does not take ownership |option|. The application is + * responsible for freeing |option| if it finishes using the object. + * + * The library code does not refer to |option| after this function + * returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_server_new2(nghttp2_session **session_ptr, + const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option); + +/** + * @function + * + * Like `nghttp2_session_client_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_client_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_client_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Like `nghttp2_session_server_new2()`, but with additional custom + * memory allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_session_server_new2()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_server_new3( + nghttp2_session **session_ptr, const nghttp2_session_callbacks *callbacks, + void *user_data, const nghttp2_option *option, nghttp2_mem *mem); + +/** + * @function + * + * Frees any resources allocated for |session|. If |session| is + * ``NULL``, this function does nothing. + */ +NGHTTP2_EXTERN void nghttp2_session_del(nghttp2_session *session); + +/** + * @function + * + * Sends pending frames to the remote peer. + * + * This function retrieves the highest prioritized frame from the + * outbound queue and sends it to the remote peer. It does this as + * many times as possible until the user callback + * :type:`nghttp2_send_callback2` returns + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`, the outbound queue + * becomes empty or flow control is triggered (remote window size + * becomes depleted or maximum number of concurrent streams is + * reached). This function calls several callback functions which are + * passed when initializing the |session|. Here is the simple time + * chart which tells when each callback is invoked: + * + * 1. Get the next frame to send from outbound queue. + * + * 2. Prepare transmission of the frame. + * + * 3. If the control frame cannot be sent because some preconditions + * are not met (e.g., request HEADERS cannot be sent after GOAWAY), + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 4. If the frame is HEADERS, PUSH_PROMISE or DATA, + * :type:`nghttp2_select_padding_callback` is invoked. + * + * 5. If the frame is request HEADERS, the stream is opened here. + * + * 6. :type:`nghttp2_before_frame_send_callback` is invoked. + * + * 7. If :enum:`nghttp2_error.NGHTTP2_ERR_CANCEL` is returned from + * :type:`nghttp2_before_frame_send_callback`, the current frame + * transmission is canceled, and + * :type:`nghttp2_on_frame_not_send_callback` is invoked. Abort + * the following steps. + * + * 8. :type:`nghttp2_send_callback2` is invoked one or more times to + * send the frame. + * + * 9. :type:`nghttp2_on_frame_send_callback` is invoked. + * + * 10. If the transmission of the frame triggers closure of the + * stream, the stream is closed and + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + */ +NGHTTP2_EXTERN int nghttp2_session_send(nghttp2_session *session); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_session_mem_send2()` instead. + * + * Returns the serialized data to send. + * + * This function behaves like `nghttp2_session_send()` except that it + * does not use :type:`nghttp2_send_callback` to transmit data. + * Instead, it assigns the pointer to the serialized data to the + * |*data_ptr| and returns its length. The other callbacks are called + * in the same way as they are in `nghttp2_session_send()`. + * + * If no data is available to send, this function returns 0. + * + * This function may not return all serialized data in one invocation. + * To get all data, call this function repeatedly until it returns 0 + * or one of negative error codes. + * + * The assigned |*data_ptr| is valid until the next call of + * `nghttp2_session_mem_send()` or `nghttp2_session_send()`. + * + * The caller must send all data before sending the next chunk of + * data. + * + * This function returns the length of the data pointed by the + * |*data_ptr| if it succeeds, or one of the following negative error + * codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * + * .. note:: + * + * This function may produce very small byte string. If that is the + * case, and application disables Nagle algorithm (``TCP_NODELAY``), + * then writing this small chunk leads to very small packet, and it + * is very inefficient. An application should be responsible to + * buffer up small chunks of data as necessary to avoid this + * situation. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_send(nghttp2_session *session, + const uint8_t **data_ptr); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Returns the serialized data to send. + * + * This function behaves like `nghttp2_session_send()` except that it + * does not use :type:`nghttp2_send_callback2` to transmit data. + * Instead, it assigns the pointer to the serialized data to the + * |*data_ptr| and returns its length. The other callbacks are called + * in the same way as they are in `nghttp2_session_send()`. + * + * If no data is available to send, this function returns 0. + * + * This function may not return all serialized data in one invocation. + * To get all data, call this function repeatedly until it returns 0 + * or one of negative error codes. + * + * The assigned |*data_ptr| is valid until the next call of + * `nghttp2_session_mem_send2()` or `nghttp2_session_send()`. + * + * The caller must send all data before sending the next chunk of + * data. + * + * This function returns the length of the data pointed by the + * |*data_ptr| if it succeeds, or one of the following negative error + * codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * + * .. note:: + * + * This function may produce very small byte string. If that is the + * case, and application disables Nagle algorithm (``TCP_NODELAY``), + * then writing this small chunk leads to very small packet, and it + * is very inefficient. An application should be responsible to + * buffer up small chunks of data as necessary to avoid this + * situation. + */ +NGHTTP2_EXTERN nghttp2_ssize +nghttp2_session_mem_send2(nghttp2_session *session, const uint8_t **data_ptr); + +/** + * @function + * + * Receives frames from the remote peer. + * + * This function receives as many frames as possible until the user + * callback :type:`nghttp2_recv_callback` returns + * :enum:`nghttp2_error.NGHTTP2_ERR_WOULDBLOCK`. This function calls + * several callback functions which are passed when initializing the + * |session|. Here is the simple time chart which tells when each + * callback is invoked: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive frame header. + * + * 2. When frame header is received, + * :type:`nghttp2_on_begin_frame_callback` is invoked. + * + * 3. If the frame is DATA frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked to receive DATA + * payload. For each chunk of data, + * :type:`nghttp2_on_data_chunk_recv_callback` is invoked. + * + * 2. If one DATA frame is completely received, + * :type:`nghttp2_on_frame_recv_callback` is invoked. If the + * reception of the frame triggers the closure of the stream, + * :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 4. If the frame is the control frame: + * + * 1. :type:`nghttp2_recv_callback` is invoked one or more times to + * receive whole frame. + * + * 2. If the received frame is valid, then following actions are + * taken. If the frame is either HEADERS or PUSH_PROMISE, + * :type:`nghttp2_on_begin_headers_callback` is invoked. Then + * :type:`nghttp2_on_header_callback` is invoked for each header + * name/value pair. For invalid header field, + * :type:`nghttp2_on_invalid_header_callback` is called. After + * all name/value pairs are emitted successfully, + * :type:`nghttp2_on_frame_recv_callback` is invoked. For other + * frames, :type:`nghttp2_on_frame_recv_callback` is invoked. + * If the reception of the frame triggers the closure of the + * stream, :type:`nghttp2_on_stream_close_callback` is invoked. + * + * 3. If the received frame is unpacked but is interpreted as + * invalid, :type:`nghttp2_on_invalid_frame_recv_callback` is + * invoked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_EOF` + * The remote peer did shutdown on the connection. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN int nghttp2_session_recv(nghttp2_session *session); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_session_mem_recv2()` instead. + * + * Processes data |in| as an input from the remote endpoint. The + * |inlen| indicates the number of bytes to receive in the |in|. + * + * This function behaves like `nghttp2_session_recv()` except that it + * does not use :type:`nghttp2_recv_callback` to receive data; the + * |in| is the only data for the invocation of this function. If all + * bytes are processed, this function returns. The other callbacks + * are called in the same way as they are in `nghttp2_session_recv()`. + * + * In the current implementation, this function always tries to + * processes |inlen| bytes of input data unless either an error occurs or + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from + * :type:`nghttp2_on_header_callback` or + * :type:`nghttp2_on_data_chunk_recv_callback`. If + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value + * includes the number of bytes which was used to produce the data or + * frame for the callback. + * + * This function returns the number of processed bytes, or one of the + * following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN ssize_t nghttp2_session_mem_recv(nghttp2_session *session, + const uint8_t *in, + size_t inlen); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Processes data |in| as an input from the remote endpoint. The + * |inlen| indicates the number of bytes to receive in the |in|. + * + * This function behaves like `nghttp2_session_recv()` except that it + * does not use :type:`nghttp2_recv_callback` to receive data; the + * |in| is the only data for the invocation of this function. If all + * bytes are processed, this function returns. The other callbacks + * are called in the same way as they are in `nghttp2_session_recv()`. + * + * In the current implementation, this function always tries to + * processes |inlen| bytes of input data unless either an error occurs or + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is returned from + * :type:`nghttp2_on_header_callback` or + * :type:`nghttp2_on_data_chunk_recv_callback`. If + * :enum:`nghttp2_error.NGHTTP2_ERR_PAUSE` is used, the return value + * includes the number of bytes which was used to produce the data or + * frame for the callback. + * + * This function returns the number of processed bytes, or one of the + * following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_CALLBACK_FAILURE` + * The callback function failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_BAD_CLIENT_MAGIC` + * Invalid client magic was detected. This error only returns + * when |session| was configured as server and + * `nghttp2_option_set_no_recv_client_magic()` is not used with + * nonzero value. + * :enum:`nghttp2_error.NGHTTP2_ERR_FLOODED` + * Flooding was detected in this HTTP/2 session, and it must be + * closed. This is most likely caused by misbehaviour of peer. + */ +NGHTTP2_EXTERN nghttp2_ssize nghttp2_session_mem_recv2(nghttp2_session *session, + const uint8_t *in, + size_t inlen); + +/** + * @function + * + * Puts back previously deferred DATA frame in the stream |stream_id| + * to the outbound queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist; or no deferred data exist. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_resume_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns nonzero value if |session| wants to receive data from the + * remote peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_read(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero value if |session| wants to send data to the remote + * peer. + * + * If both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0, the application should + * drop the connection. + */ +NGHTTP2_EXTERN int nghttp2_session_want_write(nghttp2_session *session); + +/** + * @function + * + * Returns stream_user_data for the stream |stream_id|. The + * stream_user_data is provided by `nghttp2_submit_request2()`, + * `nghttp2_submit_headers()` or + * `nghttp2_session_set_stream_user_data()`. Unless it is set using + * `nghttp2_session_set_stream_user_data()`, if the stream is + * initiated by the remote endpoint, stream_user_data is always + * ``NULL``. If the stream does not exist, this function returns + * ``NULL``. + */ +NGHTTP2_EXTERN void * +nghttp2_session_get_stream_user_data(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Sets the |stream_user_data| to the stream denoted by the + * |stream_id|. If a stream user data is already set to the stream, + * it is replaced with the |stream_user_data|. It is valid to specify + * ``NULL`` in the |stream_user_data|, which nullifies the associated + * data pointer. + * + * It is valid to set the |stream_user_data| to the stream reserved by + * PUSH_PROMISE frame. + * + * This function returns 0 if it succeeds, or one of following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The stream does not exist + */ +NGHTTP2_EXTERN int +nghttp2_session_set_stream_user_data(nghttp2_session *session, + int32_t stream_id, void *stream_user_data); + +/** + * @function + * + * Sets |user_data| to |session|, overwriting the existing user data + * specified in `nghttp2_session_client_new()`, or + * `nghttp2_session_server_new()`. + */ +NGHTTP2_EXTERN void nghttp2_session_set_user_data(nghttp2_session *session, + void *user_data); + +/** + * @function + * + * Returns the number of frames in the outbound queue. This does not + * include the deferred DATA frames. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_outbound_queue_size(nghttp2_session *session); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for the stream |stream_id|. The local + * (receive) window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective data length. In particular, if the + * local window size is reduced by submitting negative + * window_size_increment with `nghttp2_submit_window_update()`, this + * function returns the number of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_recv_data_length( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the local (receive) window size for the stream |stream_id|. + * The local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_stream_local_window_size()` to know the amount + * of data the remote endpoint can send without receiving stream level + * WINDOW_UPDATE frame. Note that each stream is still subject to the + * connection level flow control. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_effective_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the amount of flow-controlled payload (e.g., DATA) that the + * remote endpoint can send without receiving stream level + * WINDOW_UPDATE frame. It is also subject to the connection level + * flow control. So the actual amount of data to send is + * min(`nghttp2_session_get_stream_local_window_size()`, + * `nghttp2_session_get_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_local_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the number of DATA payload in bytes received without + * WINDOW_UPDATE transmission for a connection. The local (receive) + * window size can be adjusted by `nghttp2_submit_window_update()`. + * This function takes into account that and returns effective data + * length. In particular, if the local window size is reduced by + * submitting negative window_size_increment with + * `nghttp2_submit_window_update()`, this function returns the number + * of bytes less than actually received. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_effective_recv_data_length(nghttp2_session *session); + +/** + * @function + * + * Returns the local (receive) window size for a connection. The + * local window size can be adjusted by + * `nghttp2_submit_window_update()`. This function takes into account + * that and returns effective window size. + * + * This function does not take into account the amount of received + * data from the remote endpoint. Use + * `nghttp2_session_get_local_window_size()` to know the amount of + * data the remote endpoint can send without receiving + * connection-level WINDOW_UPDATE frame. Note that each stream is + * still subject to the stream level flow control. + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_effective_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the amount of flow-controlled payload (e.g., DATA) that the + * remote endpoint can send without receiving connection level + * WINDOW_UPDATE frame. Note that each stream is still subject to the + * stream level flow control (see + * `nghttp2_session_get_stream_local_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_local_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns the remote window size for a given stream |stream_id|. + * + * This is the amount of flow-controlled payload (e.g., DATA) that the + * local endpoint can send without stream level WINDOW_UPDATE. There + * is also connection level flow control, so the effective size of + * payload that the local endpoint can actually send is + * min(`nghttp2_session_get_stream_remote_window_size()`, + * `nghttp2_session_get_remote_window_size()`). + * + * This function returns -1 if it fails. + */ +NGHTTP2_EXTERN int32_t nghttp2_session_get_stream_remote_window_size( + nghttp2_session *session, int32_t stream_id); + +/** + * @function + * + * Returns the remote window size for a connection. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_remote_window_size(nghttp2_session *session); + +/** + * @function + * + * Returns 1 if local peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_local_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns 1 if remote peer half closed the given stream |stream_id|. + * Returns 0 if it did not. Returns -1 if no such stream exists. + */ +NGHTTP2_EXTERN int +nghttp2_session_get_stream_remote_close(nghttp2_session *session, + int32_t stream_id); + +/** + * @function + * + * Returns the current dynamic table size of HPACK inflater, including + * the overhead 32 bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_hd_inflate_dynamic_table_size(nghttp2_session *session); + +/** + * @function + * + * Returns the current dynamic table size of HPACK deflater including + * the overhead 32 bytes per entry described in RFC 7541. + */ +NGHTTP2_EXTERN size_t +nghttp2_session_get_hd_deflate_dynamic_table_size(nghttp2_session *session); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * The last stream ID is the minimum value between the stream ID of a + * stream for which :type:`nghttp2_on_frame_recv_callback` was called + * most recently and the last stream ID we have sent to the peer + * previously. + * + * The |error_code| is the error code of this GOAWAY frame. The + * pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * After the transmission, both `nghttp2_session_want_read()` and + * `nghttp2_session_want_write()` return 0. + * + * This function should be called when the connection should be + * terminated after sending GOAWAY. If the remaining streams should + * be processed after GOAWAY, use `nghttp2_submit_goaway()` instead. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session(nghttp2_session *session, + uint32_t error_code); + +/** + * @function + * + * Signals the session so that the connection should be terminated. + * + * This function behaves like `nghttp2_session_terminate_session()`, + * but the last stream ID can be specified by the application for fine + * grained control of stream. The HTTP/2 specification does not allow + * last_stream_id to be increased. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id we have previously sent to + * the peer. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |last_stream_id| is invalid. + */ +NGHTTP2_EXTERN int nghttp2_session_terminate_session2(nghttp2_session *session, + int32_t last_stream_id, + uint32_t error_code); + +/** + * @function + * + * Signals to the client that the server started graceful shutdown + * procedure. + * + * This function is only usable for server. If this function is + * called with client side session, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. + * + * To gracefully shutdown HTTP/2 session, server should call this + * function to send GOAWAY with last_stream_id (1u << 31) - 1. And + * after some delay (e.g., 1 RTT), send another GOAWAY with the stream + * ID that the server has some processing using + * `nghttp2_submit_goaway()`. See also + * `nghttp2_session_get_last_proc_stream_id()`. + * + * Unlike `nghttp2_submit_goaway()`, this function just sends GOAWAY + * and does nothing more. This is a mere indication to the client + * that session shutdown is imminent. The application should call + * `nghttp2_submit_goaway()` with appropriate last_stream_id after + * this call. + * + * If one or more GOAWAY frame have been already sent by either + * `nghttp2_submit_goaway()` or `nghttp2_session_terminate_session()`, + * this function has no effect. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + */ +NGHTTP2_EXTERN int nghttp2_submit_shutdown_notice(nghttp2_session *session); + +/** + * @function + * + * Returns the value of SETTINGS |id| notified by a remote endpoint. + * The |id| must be one of values defined in + * :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t nghttp2_session_get_remote_settings( + nghttp2_session *session, nghttp2_settings_id id); + +/** + * @function + * + * Returns the value of SETTINGS |id| of local endpoint acknowledged + * by the remote endpoint. The |id| must be one of the values defined + * in :enum:`nghttp2_settings_id`. + */ +NGHTTP2_EXTERN uint32_t nghttp2_session_get_local_settings( + nghttp2_session *session, nghttp2_settings_id id); + +/** + * @function + * + * Tells the |session| that next stream ID is |next_stream_id|. The + * |next_stream_id| must be equal or greater than the value returned + * by `nghttp2_session_get_next_stream_id()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |next_stream_id| is strictly less than the value + * `nghttp2_session_get_next_stream_id()` returns; or + * |next_stream_id| is invalid (e.g., even integer for client, or + * odd integer for server). + */ +NGHTTP2_EXTERN int nghttp2_session_set_next_stream_id(nghttp2_session *session, + int32_t next_stream_id); + +/** + * @function + * + * Returns the next outgoing stream ID. Notice that return type is + * uint32_t. If we run out of stream ID for this session, this + * function returns 1 << 31. + */ +NGHTTP2_EXTERN uint32_t +nghttp2_session_get_next_stream_id(nghttp2_session *session); + +/** + * @function + * + * Tells the |session| that |size| bytes for a stream denoted by + * |stream_id| were consumed by application and are ready to + * WINDOW_UPDATE. The consumed bytes are counted towards both + * connection and stream level WINDOW_UPDATE (see + * `nghttp2_session_consume_connection()` and + * `nghttp2_session_consume_stream()` to update consumption + * independently). This function is intended to be used without + * automatic window update (see + * `nghttp2_option_set_no_auto_window_update()`). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume(nghttp2_session *session, + int32_t stream_id, size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for connection level. Note that + * HTTP/2 maintains connection and stream level flow control windows + * independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_connection(nghttp2_session *session, + size_t size); + +/** + * @function + * + * Like `nghttp2_session_consume()`, but this only tells library that + * |size| bytes were consumed only for stream denoted by |stream_id|. + * Note that HTTP/2 maintains connection and stream level flow control + * windows independently. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * Automatic WINDOW_UPDATE is not disabled. + */ +NGHTTP2_EXTERN int nghttp2_session_consume_stream(nghttp2_session *session, + int32_t stream_id, + size_t size); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return 0 without doing anything. + * + * Changes priority of existing stream denoted by |stream_id|. The + * new priority specification is |pri_spec|. + * + * The priority is changed silently and instantly, and no PRIORITY + * frame will be sent to notify the peer of this change. This + * function may be useful for server to change the priority of pushed + * stream. + * + * If |session| is initialized as server, and ``pri_spec->stream_id`` + * points to the idle stream, the idle stream is created if it does + * not exist. The created idle stream will depend on root stream + * (stream 0) with weight 16. + * + * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not + * found, we use default priority instead of given |pri_spec|. That + * is make stream depend on root stream with weight 16. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is submitted via `nghttp2_submit_settings()`, this + * function does nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Attempted to depend on itself; or no stream exist for the given + * |stream_id|; or |stream_id| is 0 + */ +NGHTTP2_EXTERN int +nghttp2_session_change_stream_priority(nghttp2_session *session, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return 0 without doing anything. + * + * Creates idle stream with the given |stream_id|, and priority + * |pri_spec|. + * + * The stream creation is done without sending PRIORITY frame, which + * means that peer does not know about the existence of this idle + * stream in the local endpoint. + * + * RFC 7540 does not disallow the use of creation of idle stream with + * odd or even stream ID regardless of client or server. So this + * function can create odd or even stream ID regardless of client or + * server. But probably it is a bit safer to use the stream ID the + * local endpoint can initiate (in other words, use odd stream ID for + * client, and even stream ID for server), to avoid potential + * collision from peer's instruction. Also we can use + * `nghttp2_session_set_next_stream_id()` to avoid to open created + * idle streams accidentally if we follow this recommendation. + * + * If |session| is initialized as server, and ``pri_spec->stream_id`` + * points to the idle stream, the idle stream is created if it does + * not exist. The created idle stream will depend on root stream + * (stream 0) with weight 16. + * + * Otherwise, if stream denoted by ``pri_spec->stream_id`` is not + * found, we use default priority instead of given |pri_spec|. That + * is make stream depend on root stream with weight 16. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is submitted via `nghttp2_submit_settings()`, this + * function does nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Attempted to depend on itself; or stream denoted by |stream_id| + * already exists; or |stream_id| cannot be used to create idle + * stream (in other words, local endpoint has already opened + * stream ID greater than or equal to the given stream ID; or + * |stream_id| is 0 + */ +NGHTTP2_EXTERN int +nghttp2_session_create_idle_stream(nghttp2_session *session, int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * .. warning:: + * + * This function is deprecated in favor of + * `nghttp2_session_upgrade2()`, because this function lacks the + * parameter to tell the library the request method used in the + * original HTTP request. This information is required for client + * to validate actual response body length against content-length + * header field (see `nghttp2_option_set_no_http_messaging()`). If + * HEAD is used in request, the length of response body must be 0 + * regardless of value included in content-length header field. + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |settings_payload| is badly formed. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + void *stream_user_data); + +/** + * @function + * + * Performs post-process of HTTP Upgrade request. This function can + * be called from both client and server, but the behavior is very + * different in each other. + * + * If called from client side, the |settings_payload| must be the + * value sent in ``HTTP2-Settings`` header field and must be decoded + * by base64url decoder. The |settings_payloadlen| is the length of + * |settings_payload|. The |settings_payload| is unpacked and its + * setting values will be submitted using `nghttp2_submit_settings()`. + * This means that the client application code does not need to submit + * SETTINGS by itself. The stream with stream ID=1 is opened and the + * |stream_user_data| is used for its stream_user_data. The opened + * stream becomes half-closed (local) state. + * + * If called from server side, the |settings_payload| must be the + * value received in ``HTTP2-Settings`` header field and must be + * decoded by base64url decoder. The |settings_payloadlen| is the + * length of |settings_payload|. It is treated as if the SETTINGS + * frame with that payload is received. Thus, callback functions for + * the reception of SETTINGS frame will be invoked. The stream with + * stream ID=1 is opened. The |stream_user_data| is ignored. The + * opened stream becomes half-closed (remote). + * + * If the request method is HEAD, pass nonzero value to + * |head_request|. Otherwise, pass 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |settings_payload| is badly formed. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The stream ID 1 is already used or closed; or is not available. + */ +NGHTTP2_EXTERN int nghttp2_session_upgrade2(nghttp2_session *session, + const uint8_t *settings_payload, + size_t settings_payloadlen, + int head_request, + void *stream_user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_pack_settings_payload2()` instead. + * + * Serializes the SETTINGS values |iv| in the |buf|. The size of the + * |buf| is specified by |buflen|. The number of entries in the |iv| + * array is given by |niv|. The required space in |buf| for the |niv| + * entries is ``6*niv`` bytes and if the given buffer is too small, an + * error is returned. This function is used mainly for creating a + * SETTINGS payload to be sent with the ``HTTP2-Settings`` header + * field in an HTTP Upgrade request. The data written in |buf| is NOT + * base64url encoded and the application is responsible for encoding. + * + * This function returns the number of bytes written in |buf|, or one + * of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains duplicate settings ID or invalid value. + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_pack_settings_payload( + uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Serializes the SETTINGS values |iv| in the |buf|. The size of the + * |buf| is specified by |buflen|. The number of entries in the |iv| + * array is given by |niv|. The required space in |buf| for the |niv| + * entries is ``6*niv`` bytes and if the given buffer is too small, an + * error is returned. This function is used mainly for creating a + * SETTINGS payload to be sent with the ``HTTP2-Settings`` header + * field in an HTTP Upgrade request. The data written in |buf| is NOT + * base64url encoded and the application is responsible for encoding. + * + * This function returns the number of bytes written in |buf|, or one + * of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains duplicate settings ID or invalid value. + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN nghttp2_ssize nghttp2_pack_settings_payload2( + uint8_t *buf, size_t buflen, const nghttp2_settings_entry *iv, size_t niv); + +/** + * @function + * + * Returns string describing the |lib_error_code|. The + * |lib_error_code| must be one of the :enum:`nghttp2_error`. + */ +NGHTTP2_EXTERN const char *nghttp2_strerror(int lib_error_code); + +/** + * @function + * + * Returns string representation of HTTP/2 error code |error_code| + * (e.g., ``PROTOCOL_ERROR`` is returned if ``error_code == + * NGHTTP2_PROTOCOL_ERROR``). If string representation is unknown for + * given |error_code|, this function returns string ``unknown``. + */ +NGHTTP2_EXTERN const char *nghttp2_http2_strerror(uint32_t error_code); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * + * Initializes |pri_spec| with the |stream_id| of the stream to depend + * on with |weight| and its exclusive flag. If |exclusive| is + * nonzero, exclusive flag is set. + * + * The |weight| must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. + */ +NGHTTP2_EXTERN void nghttp2_priority_spec_init(nghttp2_priority_spec *pri_spec, + int32_t stream_id, + int32_t weight, int exclusive); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * + * Initializes |pri_spec| with the default values. The default values + * are: stream_id = 0, weight = :macro:`NGHTTP2_DEFAULT_WEIGHT` and + * exclusive = 0. + */ +NGHTTP2_EXTERN void +nghttp2_priority_spec_default_init(nghttp2_priority_spec *pri_spec); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * + * Returns nonzero if the |pri_spec| is filled with default values. + */ +NGHTTP2_EXTERN int +nghttp2_priority_spec_check_default(const nghttp2_priority_spec *pri_spec); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_submit_request2()` instead. + * + * Submits HEADERS frame and optionally one or more DATA frames. + * + * The |pri_spec| is a deprecated priority specification of this + * request. ``NULL`` means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. + * + * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` + * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes + * :macro:`NGHTTP2_MAX_WEIGHT`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, |pri_spec| is + * ignored, and treated as if ``NULL`` is specified. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * request HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. In this case, a method that allows + * request message bodies + * (https://tools.ietf.org/html/rfc7231#section-4) must be specified + * with ``:method`` key in |nva| (e.g. ``POST``). This function does + * not take ownership of the |data_prd|. The function copies the + * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have + * END_STREAM set. The |stream_user_data| is data associated to the + * stream opened by this request and can be an arbitrary pointer, + * which can be retrieved later by + * `nghttp2_session_get_stream_user_data()`. + * + * This function returns assigned stream ID if it succeeds, or one of + * the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Trying to depend on itself (new stream ID equals + * ``pri_spec->stream_id``). + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds. But + * that stream is not created yet. The application must not submit + * frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. This means `nghttp2_session_get_stream_user_data()` does + * not work before the callback. But + * `nghttp2_session_set_stream_user_data()` handles this situation + * specially, and it can set data to a stream during this period. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_request( + nghttp2_session *session, const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider *data_prd, + void *stream_user_data); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Submits HEADERS frame and optionally one or more DATA frames. + * + * The |pri_spec| is a deprecated priority specification of this + * request. ``NULL`` means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. In the future release + * after the end of 2024, this function will ignore |pri_spec| and + * behave as if ``NULL`` is given. + * + * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` + * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes + * :macro:`NGHTTP2_MAX_WEIGHT`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, |pri_spec| is + * ignored, and treated as if ``NULL`` is specified. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * request HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. In this case, a method that allows + * request message bodies + * (https://tools.ietf.org/html/rfc7231#section-4) must be specified + * with ``:method`` key in |nva| (e.g. ``POST``). This function does + * not take ownership of the |data_prd|. The function copies the + * members of the |data_prd|. If |data_prd| is ``NULL``, HEADERS have + * END_STREAM set. The |stream_user_data| is data associated to the + * stream opened by this request and can be an arbitrary pointer, + * which can be retrieved later by + * `nghttp2_session_get_stream_user_data()`. + * + * This function returns assigned stream ID if it succeeds, or one of + * the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Trying to depend on itself (new stream ID equals + * ``pri_spec->stream_id``). + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds. But + * that stream is not created yet. The application must not submit + * frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. This means `nghttp2_session_get_stream_user_data()` does + * not work before the callback. But + * `nghttp2_session_set_stream_user_data()` handles this situation + * specially, and it can set data to a stream during this period. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_request2( + nghttp2_session *session, const nghttp2_priority_spec *pri_spec, + const nghttp2_nv *nva, size_t nvlen, const nghttp2_data_provider2 *data_prd, + void *stream_user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_submit_response2()` instead. + * + * Submits response HEADERS frame and optionally one or more DATA + * frames against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * response HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. This function does not take ownership + * of the |data_prd|. The function copies the members of the + * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have + * END_STREAM flag set. + * + * This method can be used as normal HTTP response and push response. + * When pushing a resource using this function, the |session| must be + * configured using `nghttp2_session_server_new()` or its variants and + * the target stream denoted by the |stream_id| must be reserved using + * `nghttp2_submit_push_promise()`. + * + * To send non-final response headers (e.g., HTTP status 101), don't + * use this function because this function half-closes the outbound + * stream. Instead, use `nghttp2_submit_headers()` for this purpose. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. Normally, this does not happen, but when + * application wrongly calls `nghttp2_submit_response()` twice, + * this may happen. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is client session. + * + * .. warning:: + * + * Calling this function twice for the same stream ID may lead to + * program crash. It is generally considered to a programming error + * to commit response twice. + */ +NGHTTP2_EXTERN int +nghttp2_submit_response(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider *data_prd); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Submits response HEADERS frame and optionally one or more DATA + * frames against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * HTTP/2 specification has requirement about header fields in the + * response HEADERS. See the specification for more details. + * + * If |data_prd| is not ``NULL``, it provides data which will be sent + * in subsequent DATA frames. This function does not take ownership + * of the |data_prd|. The function copies the members of the + * |data_prd|. If |data_prd| is ``NULL``, HEADERS will have + * END_STREAM flag set. + * + * This method can be used as normal HTTP response and push response. + * When pushing a resource using this function, the |session| must be + * configured using `nghttp2_session_server_new()` or its variants and + * the target stream denoted by the |stream_id| must be reserved using + * `nghttp2_submit_push_promise()`. + * + * To send non-final response headers (e.g., HTTP status 101), don't + * use this function because this function half-closes the outbound + * stream. Instead, use `nghttp2_submit_headers()` for this purpose. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. Normally, this does not happen, but when + * application wrongly calls `nghttp2_submit_response2()` twice, + * this may happen. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |session| is client session. + * + * .. warning:: + * + * Calling this function twice for the same stream ID may lead to + * program crash. It is generally considered to a programming error + * to commit response twice. + */ +NGHTTP2_EXTERN int +nghttp2_submit_response2(nghttp2_session *session, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, + const nghttp2_data_provider2 *data_prd); + +/** + * @function + * + * Submits trailer fields HEADERS against the stream |stream_id|. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application must not include pseudo-header + * fields (headers whose names starts with ":") in |nva|. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * For server, trailer fields must follow response HEADERS or response + * DATA without END_STREAM flat set. The library does not enforce + * this requirement, and applications should do this for themselves. + * If `nghttp2_submit_trailer()` is called before any response HEADERS + * submission (usually by `nghttp2_submit_response2()`), the content + * of |nva| will be sent as response headers, which will result in + * error. + * + * This function has the same effect with `nghttp2_submit_headers()`, + * with flags = :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` and both + * pri_spec and stream_user_data to NULL. + * + * To submit trailer fields after `nghttp2_submit_response2()` is + * called, the application has to specify + * :type:`nghttp2_data_provider2` to `nghttp2_submit_response2()`. + * Inside of :type:`nghttp2_data_source_read_callback2`, when setting + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_EOF`, also set + * :enum:`nghttp2_data_flag.NGHTTP2_DATA_FLAG_NO_END_STREAM`. After + * that, the application can send trailer fields using + * `nghttp2_submit_trailer()`. `nghttp2_submit_trailer()` can be used + * inside :type:`nghttp2_data_source_read_callback2`. + * + * This function returns 0 if it succeeds and |stream_id| is -1. + * Otherwise, this function returns 0 if it succeeds, or one of the + * following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_trailer(nghttp2_session *session, + int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Submits HEADERS frame. The |flags| is bitwise OR of the + * following values: + * + * * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` + * + * If |flags| includes :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, + * this frame has END_STREAM flag set. + * + * The library handles the CONTINUATION frame internally and it + * correctly sets END_HEADERS to the last sequence of the PUSH_PROMISE + * or CONTINUATION frame. + * + * If the |stream_id| is -1, this frame is assumed as request (i.e., + * request HEADERS frame which opens new stream). In this case, the + * assigned stream ID will be returned. Otherwise, specify stream ID + * in |stream_id|. + * + * The |pri_spec| is a deprecated priority specification of this + * request. ``NULL`` means the default priority (see + * `nghttp2_priority_spec_default_init()`). To specify the priority, + * use `nghttp2_priority_spec_init()`. If |pri_spec| is not ``NULL``, + * this function will copy its data members. In the future release + * after the end of 2024, this function will ignore |pri_spec| and + * behave as if ``NULL`` is given. + * + * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` + * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes :macro:`NGHTTP2_MAX_WEIGHT`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, |pri_spec| is + * ignored, and treated as if ``NULL`` is specified. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * The |stream_user_data| is a pointer to an arbitrary data which is + * associated to the stream this frame will open. Therefore it is + * only used if this frame opens streams, in other words, it changes + * stream state from idle or reserved to open. + * + * This function is low-level in a sense that the application code can + * specify flags directly. For usual HTTP request, + * `nghttp2_submit_request2()` is useful. Likewise, for HTTP + * response, prefer `nghttp2_submit_response2()`. + * + * This function returns newly assigned stream ID if it succeeds and + * |stream_id| is -1. Otherwise, this function returns 0 if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; or trying to depend on itself (stream ID + * equals ``pri_spec->stream_id``). + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. This happens if stream denoted by |stream_id| + * is in reserved state. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * The |stream_id| is -1, and |session| is server session. + * + * .. warning:: + * + * This function returns assigned stream ID if it succeeds and + * |stream_id| is -1. But that stream is not opened yet. The + * application must not submit frame to that stream ID before + * :type:`nghttp2_before_frame_send_callback` is called for this + * frame. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_headers( + nghttp2_session *session, uint8_t flags, int32_t stream_id, + const nghttp2_priority_spec *pri_spec, const nghttp2_nv *nva, size_t nvlen, + void *stream_user_data); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_submit_data2()` instead. + * + * Submits one or more DATA frames to the stream |stream_id|. The + * data to be sent are provided by |data_prd|. If |flags| contains + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame + * has END_STREAM flag set. + * + * This function does not take ownership of the |data_prd|. The + * function copies the members of the |data_prd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` + * The stream was already closed; or the |stream_id| is invalid. + * + * .. note:: + * + * Currently, only one DATA or HEADERS is allowed for a stream at a + * time. Submitting these frames more than once before first DATA + * or HEADERS is finished results in + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code. The + * earliest callback which tells that previous frame is done is + * :type:`nghttp2_on_frame_send_callback`. In side that callback, + * new data can be submitted using `nghttp2_submit_data()`. Of + * course, all data except for last one must not have + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in |flags|. + * This sounds a bit complicated, and we recommend to use + * `nghttp2_submit_request()` and `nghttp2_submit_response()` to + * avoid this cascading issue. The experience shows that for HTTP + * use, these two functions are enough to implement both client and + * server. + */ +NGHTTP2_EXTERN int nghttp2_submit_data(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider *data_prd); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Submits one or more DATA frames to the stream |stream_id|. The + * data to be sent are provided by |data_prd|. If |flags| contains + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM`, the last DATA frame + * has END_STREAM flag set. + * + * This function does not take ownership of the |data_prd|. The + * function copies the members of the |data_prd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` + * DATA or HEADERS has been already submitted and not fully + * processed yet. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` + * The stream was already closed; or the |stream_id| is invalid. + * + * .. note:: + * + * Currently, only one DATA or HEADERS is allowed for a stream at a + * time. Submitting these frames more than once before first DATA + * or HEADERS is finished results in + * :enum:`nghttp2_error.NGHTTP2_ERR_DATA_EXIST` error code. The + * earliest callback which tells that previous frame is done is + * :type:`nghttp2_on_frame_send_callback`. In side that callback, + * new data can be submitted using `nghttp2_submit_data2()`. Of + * course, all data except for last one must not have + * :enum:`nghttp2_flag.NGHTTP2_FLAG_END_STREAM` flag set in |flags|. + * This sounds a bit complicated, and we recommend to use + * `nghttp2_submit_request2()` and `nghttp2_submit_response2()` to + * avoid this cascading issue. The experience shows that for HTTP + * use, these two functions are enough to implement both client and + * server. + */ +NGHTTP2_EXTERN int nghttp2_submit_data2(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_data_provider2 *data_prd); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return 0 without doing anything. + * + * Submits PRIORITY frame to change the priority of stream |stream_id| + * to the priority specification |pri_spec|. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |pri_spec| is a deprecated priority specification of this + * request. ``NULL`` is not allowed for this function. To specify the + * priority, use `nghttp2_priority_spec_init()`. This function will + * copy its data members. + * + * The ``pri_spec->weight`` must be in [:macro:`NGHTTP2_MIN_WEIGHT`, + * :macro:`NGHTTP2_MAX_WEIGHT`], inclusive. If ``pri_spec->weight`` + * is strictly less than :macro:`NGHTTP2_MIN_WEIGHT`, it becomes + * :macro:`NGHTTP2_MIN_WEIGHT`. If it is strictly greater than + * :macro:`NGHTTP2_MAX_WEIGHT`, it becomes + * :macro:`NGHTTP2_MAX_WEIGHT`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is received by a remote endpoint, this function does + * nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; or the |pri_spec| is NULL; or trying to + * depend on itself. + */ +NGHTTP2_EXTERN int +nghttp2_submit_priority(nghttp2_session *session, uint8_t flags, + int32_t stream_id, + const nghttp2_priority_spec *pri_spec); + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_DEFAULT_URGENCY` is the default urgency + * level for :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_DEFAULT_URGENCY 3 + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_URGENCY_HIGH` is the highest urgency level + * for :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_URGENCY_HIGH 0 + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW` is the lowest urgency level for + * :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_URGENCY_LOW 7 + +/** + * @macro + * + * :macro:`NGHTTP2_EXTPRI_URGENCY_LEVELS` is the number of urgency + * levels for :rfc:`9218` extensible priorities. + */ +#define NGHTTP2_EXTPRI_URGENCY_LEVELS (NGHTTP2_EXTPRI_URGENCY_LOW + 1) + +/** + * @struct + * + * :type:`nghttp2_extpri` is :rfc:`9218` extensible priorities + * specification for a stream. + */ +typedef struct nghttp2_extpri { + /** + * :member:`urgency` is the urgency of a stream, it must be in + * [:macro:`NGHTTP2_EXTPRI_URGENCY_HIGH`, + * :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`], inclusive, and 0 is the + * highest urgency. + */ + uint32_t urgency; + /** + * :member:`inc` indicates that a content can be processed + * incrementally or not. If inc is 0, it cannot be processed + * incrementally. If inc is 1, it can be processed incrementally. + * Other value is not permitted. + */ + int inc; +} nghttp2_extpri; + +/** + * @function + * + * Submits RST_STREAM frame to cancel/reject the stream |stream_id| + * with the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_rst_stream(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + uint32_t error_code); + +/** + * @function + * + * Stores local settings and submits SETTINGS frame. The |iv| is the + * pointer to the array of :type:`nghttp2_settings_entry`. The |niv| + * indicates the number of :type:`nghttp2_settings_entry`. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * This function does not take ownership of the |iv|. This function + * copies all the elements in the |iv|. + * + * While updating individual stream's local window size, if the window + * size becomes strictly larger than NGHTTP2_MAX_WINDOW_SIZE, + * RST_STREAM is issued against such a stream. + * + * SETTINGS with :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` is + * automatically submitted by the library and application could not + * send it at its will. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |iv| contains invalid value (e.g., initial window size + * strictly greater than (1 << 31) - 1. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_settings(nghttp2_session *session, + uint8_t flags, + const nghttp2_settings_entry *iv, + size_t niv); + +/** + * @function + * + * Submits PUSH_PROMISE frame. + * + * The |flags| is currently ignored. The library handles the + * CONTINUATION frame internally and it correctly sets END_HEADERS to + * the last sequence of the PUSH_PROMISE or CONTINUATION frame. + * + * The |stream_id| must be client initiated stream ID. + * + * The |nva| is an array of name/value pair :type:`nghttp2_nv` with + * |nvlen| elements. The application is responsible to include + * required pseudo-header fields (header field whose name starts with + * ":") in |nva| and must place pseudo-headers before regular header + * fields. + * + * This function creates copies of all name/value pairs in |nva|. It + * also lower-cases all names in |nva|. The order of elements in + * |nva| is preserved. For header fields with + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME` and + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_VALUE` are set, + * header field name and value are not copied respectively. With + * :enum:`nghttp2_nv_flag.NGHTTP2_NV_FLAG_NO_COPY_NAME`, application + * is responsible to pass header field name in lowercase. The + * application should maintain the references to them until + * :type:`nghttp2_on_frame_send_callback` or + * :type:`nghttp2_on_frame_not_send_callback` is called. + * + * The |promised_stream_user_data| is a pointer to an arbitrary data + * which is associated to the promised stream this frame will open and + * make it in reserved state. It is available using + * `nghttp2_session_get_stream_user_data()`. The application can + * access it in :type:`nghttp2_before_frame_send_callback` and + * :type:`nghttp2_on_frame_send_callback` of this frame. + * + * The client side is not allowed to use this function. + * + * To submit response headers and data, use + * `nghttp2_submit_response2()`. + * + * This function returns assigned promised stream ID if it succeeds, + * or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_PROTO` + * This function was invoked when |session| is initialized as + * client. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_ID_NOT_AVAILABLE` + * No stream ID is available because maximum stream ID was + * reached. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is 0; The |stream_id| does not designate stream + * that peer initiated. + * :enum:`nghttp2_error.NGHTTP2_ERR_STREAM_CLOSED` + * The stream was already closed; or the |stream_id| is invalid. + * + * .. warning:: + * + * This function returns assigned promised stream ID if it succeeds. + * As of 1.16.0, stream object for pushed resource is created when + * this function succeeds. In that case, the application can submit + * push response for the promised frame. + * + * In 1.15.0 or prior versions, pushed stream is not opened yet when + * this function succeeds. The application must not submit frame to + * that stream ID before :type:`nghttp2_before_frame_send_callback` + * is called for this frame. + * + */ +NGHTTP2_EXTERN int32_t nghttp2_submit_push_promise( + nghttp2_session *session, uint8_t flags, int32_t stream_id, + const nghttp2_nv *nva, size_t nvlen, void *promised_stream_user_data); + +/** + * @function + * + * Submits PING frame. You don't have to send PING back when you + * received PING frame. The library automatically submits PING frame + * in this case. + * + * The |flags| is bitwise OR of 0 or more of the following value. + * + * * :enum:`nghttp2_flag.NGHTTP2_FLAG_ACK` + * + * Unless `nghttp2_option_set_no_auto_ping_ack()` is used, the |flags| + * should be :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * If the |opaque_data| is non ``NULL``, then it should point to the 8 + * bytes array of memory to specify opaque data to send with PING + * frame. If the |opaque_data| is ``NULL``, zero-cleared 8 bytes will + * be sent as opaque data. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_ping(nghttp2_session *session, uint8_t flags, + const uint8_t *opaque_data); + +/** + * @function + * + * Submits GOAWAY frame with the last stream ID |last_stream_id| and + * the error code |error_code|. + * + * The pre-defined error code is one of :enum:`nghttp2_error_code`. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |last_stream_id| is peer's stream ID or 0. So if |session| is + * initialized as client, |last_stream_id| must be even or 0. If + * |session| is initialized as server, |last_stream_id| must be odd or + * 0. + * + * The HTTP/2 specification says last_stream_id must not be increased + * from the value previously sent. So the actual value sent as + * last_stream_id is the minimum value between the given + * |last_stream_id| and the last_stream_id previously sent to the + * peer. + * + * If the |opaque_data| is not ``NULL`` and |opaque_data_len| is not + * zero, those data will be sent as additional debug data. The + * library makes a copy of the memory region pointed by |opaque_data| + * with the length |opaque_data_len|, so the caller does not need to + * keep this memory after the return of this function. If the + * |opaque_data_len| is 0, the |opaque_data| could be ``NULL``. + * + * After successful transmission of GOAWAY, following things happen. + * All incoming streams having strictly more than |last_stream_id| are + * closed. All incoming HEADERS which starts new stream are simply + * ignored. After all active streams are handled, both + * `nghttp2_session_want_read()` and `nghttp2_session_want_write()` + * return 0 and the application can close session. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |opaque_data_len| is too large; the |last_stream_id| is + * invalid. + */ +NGHTTP2_EXTERN int nghttp2_submit_goaway(nghttp2_session *session, + uint8_t flags, int32_t last_stream_id, + uint32_t error_code, + const uint8_t *opaque_data, + size_t opaque_data_len); + +/** + * @function + * + * Returns the last stream ID of a stream for which + * :type:`nghttp2_on_frame_recv_callback` was invoked most recently. + * The returned value can be used as last_stream_id parameter for + * `nghttp2_submit_goaway()` and + * `nghttp2_session_terminate_session2()`. + * + * This function always succeeds. + */ +NGHTTP2_EXTERN int32_t +nghttp2_session_get_last_proc_stream_id(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero if new request can be sent from local endpoint. + * + * This function return 0 if request is not allowed for this session. + * There are several reasons why request is not allowed. Some of the + * reasons are: session is server; stream ID has been spent; GOAWAY + * has been sent or received. + * + * The application can call `nghttp2_submit_request2()` without + * consulting this function. In that case, + * `nghttp2_submit_request2()` may return error. Or, request is + * failed to sent, and :type:`nghttp2_on_stream_close_callback` is + * called. + */ +NGHTTP2_EXTERN int +nghttp2_session_check_request_allowed(nghttp2_session *session); + +/** + * @function + * + * Returns nonzero if |session| is initialized as server side session. + */ +NGHTTP2_EXTERN int +nghttp2_session_check_server_session(nghttp2_session *session); + +/** + * @function + * + * Submits WINDOW_UPDATE frame. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |stream_id| is the stream ID to send this WINDOW_UPDATE. To + * send connection level WINDOW_UPDATE, specify 0 to |stream_id|. + * + * If the |window_size_increment| is positive, the WINDOW_UPDATE with + * that value as window_size_increment is queued. If the + * |window_size_increment| is larger than the received bytes from the + * remote endpoint, the local window size is increased by that + * difference. If the sole purpose is to increase the local window + * size, consider to use `nghttp2_session_set_local_window_size()`. + * + * If the |window_size_increment| is negative, the local window size + * is decreased by -|window_size_increment|. If automatic + * WINDOW_UPDATE is enabled + * (`nghttp2_option_set_no_auto_window_update()`), and the library + * decided that the WINDOW_UPDATE should be submitted, then + * WINDOW_UPDATE is queued with the current received bytes count. If + * the sole purpose is to decrease the local window size, consider to + * use `nghttp2_session_set_local_window_size()`. + * + * If the |window_size_increment| is 0, the function does nothing and + * returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_FLOW_CONTROL` + * The local window size overflow or gets negative. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int nghttp2_submit_window_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + int32_t window_size_increment); + +/** + * @function + * + * Set local window size (local endpoints's window size) to the given + * |window_size| for the given stream denoted by |stream_id|. To + * change connection level window size, specify 0 to |stream_id|. To + * increase window size, this function may submit WINDOW_UPDATE frame + * to transmission queue. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * This sounds similar to `nghttp2_submit_window_update()`, but there + * are 2 differences. The first difference is that this function + * takes the absolute value of window size to set, rather than the + * delta. To change the window size, this may be easier to use since + * the application just declares the intended window size, rather than + * calculating delta. The second difference is that + * `nghttp2_submit_window_update()` affects the received bytes count + * which has not acked yet. By the specification of + * `nghttp2_submit_window_update()`, to strictly increase the local + * window size, we have to submit delta including all received bytes + * count, which might not be desirable in some cases. On the other + * hand, this function does not affect the received bytes count. It + * just sets the local window size to the given value. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |stream_id| is negative. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_session_set_local_window_size(nghttp2_session *session, uint8_t flags, + int32_t stream_id, int32_t window_size); + +/** + * @function + * + * Submits extension frame. + * + * Application can pass arbitrary frame flags and stream ID in |flags| + * and |stream_id| respectively. The |payload| is opaque pointer, and + * it can be accessible though ``frame->ext.payload`` in + * :type:`nghttp2_pack_extension_callback2`. The library will not own + * passed |payload| pointer. + * + * The application must set :type:`nghttp2_pack_extension_callback2` + * using `nghttp2_session_callbacks_set_pack_extension_callback2()`. + * + * The application should retain the memory pointed by |payload| until + * the transmission of extension frame is done (which is indicated by + * :type:`nghttp2_on_frame_send_callback`), or transmission fails + * (which is indicated by :type:`nghttp2_on_frame_not_send_callback`). + * If application does not touch this memory region after packing it + * into a wire format, application can free it inside + * :type:`nghttp2_pack_extension_callback2`. + * + * The standard HTTP/2 frame cannot be sent with this function, so + * |type| must be strictly grater than 0x9. Otherwise, this function + * will fail with error code + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * If :type:`nghttp2_pack_extension_callback2` is not set. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * If |type| specifies standard HTTP/2 frame type. The frame + * types in the rage [0x0, 0x9], both inclusive, are standard + * HTTP/2 frame type, and cannot be sent using this function. + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory + */ +NGHTTP2_EXTERN int nghttp2_submit_extension(nghttp2_session *session, + uint8_t type, uint8_t flags, + int32_t stream_id, void *payload); + +/** + * @struct + * + * The payload of ALTSVC frame. ALTSVC frame is a non-critical + * extension to HTTP/2. If this frame is received, and + * `nghttp2_option_set_user_recv_extension_type()` is not set, and + * `nghttp2_option_set_builtin_recv_extension_type()` is set for + * :enum:`nghttp2_frame_type.NGHTTP2_ALTSVC`, + * ``nghttp2_extension.payload`` will point to this struct. + * + * It has the following members: + */ +typedef struct { + /** + * The pointer to origin which this alternative service is + * associated with. This is not necessarily NULL-terminated. + */ + uint8_t *origin; + /** + * The length of the |origin|. + */ + size_t origin_len; + /** + * The pointer to Alt-Svc field value contained in ALTSVC frame. + * This is not necessarily NULL-terminated. + */ + uint8_t *field_value; + /** + * The length of the |field_value|. + */ + size_t field_value_len; +} nghttp2_ext_altsvc; + +/** + * @function + * + * Submits ALTSVC frame. + * + * ALTSVC frame is a non-critical extension to HTTP/2, and defined in + * `RFC 7383 `_. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |origin| points to the origin this alternative service is + * associated with. The |origin_len| is the length of the origin. If + * |stream_id| is 0, the origin must be specified. If |stream_id| is + * not zero, the origin must be empty (in other words, |origin_len| + * must be 0). + * + * The ALTSVC frame is only usable from server side. If this function + * is invoked with client side session, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The function is called from client side session + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The sum of |origin_len| and |field_value_len| is larger than + * 16382; or |origin_len| is 0 while |stream_id| is 0; or + * |origin_len| is not 0 while |stream_id| is not 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_altsvc(nghttp2_session *session, + uint8_t flags, int32_t stream_id, + const uint8_t *origin, + size_t origin_len, + const uint8_t *field_value, + size_t field_value_len); + +/** + * @struct + * + * The single entry of an origin. + */ +typedef struct { + /** + * The pointer to origin. No validation is made against this field + * by the library. This is not necessarily NULL-terminated. + */ + uint8_t *origin; + /** + * The length of the |origin|. + */ + size_t origin_len; +} nghttp2_origin_entry; + +/** + * @struct + * + * The payload of ORIGIN frame. ORIGIN frame is a non-critical + * extension to HTTP/2 and defined by `RFC 8336 + * `_. + * + * If this frame is received, and + * `nghttp2_option_set_user_recv_extension_type()` is not set, and + * `nghttp2_option_set_builtin_recv_extension_type()` is set for + * :enum:`nghttp2_frame_type.NGHTTP2_ORIGIN`, + * ``nghttp2_extension.payload`` will point to this struct. + * + * It has the following members: + */ +typedef struct { + /** + * The number of origins contained in |ov|. + */ + size_t nov; + /** + * The pointer to the array of origins contained in ORIGIN frame. + */ + nghttp2_origin_entry *ov; +} nghttp2_ext_origin; + +/** + * @function + * + * Submits ORIGIN frame. + * + * ORIGIN frame is a non-critical extension to HTTP/2 and defined by + * `RFC 8336 `_. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |ov| points to the array of origins. The |nov| specifies the + * number of origins included in |ov|. This function creates copies + * of all elements in |ov|. + * + * The ORIGIN frame is only usable by a server. If this function is + * invoked with client side session, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The function is called from client side session. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * There are too many origins, or an origin is too large to fit + * into a default frame payload. + */ +NGHTTP2_EXTERN int nghttp2_submit_origin(nghttp2_session *session, + uint8_t flags, + const nghttp2_origin_entry *ov, + size_t nov); + +/** + * @struct + * + * The payload of PRIORITY_UPDATE frame. PRIORITY_UPDATE frame is a + * non-critical extension to HTTP/2. If this frame is received, and + * `nghttp2_option_set_user_recv_extension_type()` is not set, and + * `nghttp2_option_set_builtin_recv_extension_type()` is set for + * :enum:`nghttp2_frame_type.NGHTTP2_PRIORITY_UPDATE`, + * ``nghttp2_extension.payload`` will point to this struct. + * + * It has the following members: + */ +typedef struct { + /** + * The stream ID of the stream whose priority is updated. + */ + int32_t stream_id; + /** + * The pointer to Priority field value. It is not necessarily + * NULL-terminated. + */ + uint8_t *field_value; + /** + * The length of the :member:`field_value`. + */ + size_t field_value_len; +} nghttp2_ext_priority_update; + +/** + * @function + * + * Submits PRIORITY_UPDATE frame. + * + * PRIORITY_UPDATE frame is a non-critical extension to HTTP/2, and + * defined in :rfc:`9218#section-7.1`. + * + * The |flags| is currently ignored and should be + * :enum:`nghttp2_flag.NGHTTP2_FLAG_NONE`. + * + * The |stream_id| is the ID of stream which is prioritized. The + * |field_value| points to the Priority field value. The + * |field_value_len| is the length of the Priority field value. + * + * If this function is called by server, + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` is returned. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 0 is received by a remote endpoint (or it is omitted), + * this function does nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The function is called from server side session + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * The |field_value_len| is larger than 16380; or |stream_id| is + * 0. + */ +NGHTTP2_EXTERN int nghttp2_submit_priority_update(nghttp2_session *session, + uint8_t flags, + int32_t stream_id, + const uint8_t *field_value, + size_t field_value_len); + +/** + * @function + * + * Changes the priority of the existing stream denoted by |stream_id|. + * The new priority is |extpri|. This function is meant to be used by + * server for :rfc:`9218` extensible prioritization scheme. + * + * If |session| is initialized as client, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. For client, use + * `nghttp2_submit_priority_update()` instead. + * + * If :member:`extpri->urgency ` is out of + * bound, it is set to :macro:`NGHTTP2_EXTPRI_URGENCY_LOW`. + * + * If |ignore_client_signal| is nonzero, server starts to ignore + * client priority signals for this stream. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is not submitted via `nghttp2_submit_settings()`, + * this function does nothing and returns 0. + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * |stream_id| is zero; or a stream denoted by |stream_id| is not + * found. + */ +NGHTTP2_EXTERN int nghttp2_session_change_extpri_stream_priority( + nghttp2_session *session, int32_t stream_id, const nghttp2_extpri *extpri, + int ignore_client_signal); + +/** + * @function + * + * Stores the stream priority of the existing stream denoted by + * |stream_id| in the object pointed by |extpri|. This function is + * meant to be used by server for :rfc:`9218` extensible + * prioritization scheme. + * + * If |session| is initialized as client, this function returns + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE`. + * + * If + * :enum:`nghttp2_settings_id.NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES` + * of value of 1 is not submitted via `nghttp2_submit_settings()`, + * this function does nothing and returns 0. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` + * The |session| is initialized as client. + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * |stream_id| is zero; or a stream denoted by |stream_id| is not + * found. + */ +NGHTTP2_EXTERN int nghttp2_session_get_extpri_stream_priority( + nghttp2_session *session, nghttp2_extpri *extpri, int32_t stream_id); + +/** + * @function + * + * Parses Priority header field value pointed by |value| of length + * |len|, and stores the result in the object pointed by |extpri|. + * Priority header field is defined in :rfc:`9218`. + * + * This function does not initialize the object pointed by |extpri| + * before storing the result. It only assigns the values that the + * parser correctly extracted to fields. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_ARGUMENT` + * Failed to parse the header field value. + */ +NGHTTP2_EXTERN int nghttp2_extpri_parse_priority(nghttp2_extpri *extpri, + const uint8_t *value, + size_t len); + +/** + * @function + * + * Compares ``lhs->name`` of length ``lhs->namelen`` bytes and + * ``rhs->name`` of length ``rhs->namelen`` bytes. Returns negative + * integer if ``lhs->name`` is found to be less than ``rhs->name``; or + * returns positive integer if ``lhs->name`` is found to be greater + * than ``rhs->name``; or returns 0 otherwise. + */ +NGHTTP2_EXTERN int nghttp2_nv_compare_name(const nghttp2_nv *lhs, + const nghttp2_nv *rhs); + +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_select_alpn` instead. + * + * A helper function for dealing with ALPN in server side. The |in| + * contains peer's protocol list in preferable order. The format of + * |in| is length-prefixed and not null-terminated. For example, + * ``h2`` and ``http/1.1`` stored in |in| like this:: + * + * in[0] = 2 + * in[1..2] = "h2" + * in[3] = 8 + * in[4..11] = "http/1.1" + * inlen = 12 + * + * The selection algorithm is as follows: + * + * 1. If peer's list contains HTTP/2 protocol the library supports, + * it is selected and returns 1. The following step is not taken. + * + * 2. If peer's list contains ``http/1.1``, this function selects + * ``http/1.1`` and returns 0. The following step is not taken. + * + * 3. This function selects nothing and returns -1 (So called + * non-overlap case). In this case, |out| and |outlen| are left + * untouched. + * + * Selecting ``h2`` means that ``h2`` is written into |*out| and its + * length (which is 2) is assigned to |*outlen|. + * + * For ALPN, refer to https://tools.ietf.org/html/rfc7301 + * + * To use this method you should do something like:: + * + * static int alpn_select_proto_cb(SSL* ssl, + * const unsigned char **out, + * unsigned char *outlen, + * const unsigned char *in, + * unsigned int inlen, + * void *arg) + * { + * int rv; + * rv = nghttp2_select_next_protocol((unsigned char**)out, outlen, + * in, inlen); + * if (rv == -1) { + * return SSL_TLSEXT_ERR_NOACK; + * } + * if (rv == 1) { + * ((MyType*)arg)->http2_selected = 1; + * } + * return SSL_TLSEXT_ERR_OK; + * } + * ... + * SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj); + * + */ +NGHTTP2_EXTERN int nghttp2_select_next_protocol(unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen); + +/** + * @function + * + * A helper function for dealing with ALPN in server side. The |in| + * contains peer's protocol list in preferable order. The format of + * |in| is length-prefixed and not null-terminated. For example, + * ``h2`` and ``http/1.1`` stored in |in| like this:: + * + * in[0] = 2 + * in[1..2] = "h2" + * in[3] = 8 + * in[4..11] = "http/1.1" + * inlen = 12 + * + * The selection algorithm is as follows: + * + * 1. If peer's list contains HTTP/2 protocol the library supports, + * it is selected and returns 1. The following step is not taken. + * + * 2. If peer's list contains ``http/1.1``, this function selects + * ``http/1.1`` and returns 0. The following step is not taken. + * + * 3. This function selects nothing and returns -1 (So called + * non-overlap case). In this case, |out| and |outlen| are left + * untouched. + * + * Selecting ``h2`` means that ``h2`` is written into |*out| and its + * length (which is 2) is assigned to |*outlen|. + * + * For ALPN, refer to https://tools.ietf.org/html/rfc7301 + * + * To use this method you should do something like:: + * + * static int alpn_select_proto_cb(SSL* ssl, + * const unsigned char **out, + * unsigned char *outlen, + * const unsigned char *in, + * unsigned int inlen, + * void *arg) + * { + * int rv; + * rv = nghttp2_select_alpn(out, outlen, in, inlen); + * if (rv == -1) { + * return SSL_TLSEXT_ERR_NOACK; + * } + * if (rv == 1) { + * ((MyType*)arg)->http2_selected = 1; + * } + * return SSL_TLSEXT_ERR_OK; + * } + * ... + * SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, my_obj); + * + */ +NGHTTP2_EXTERN int nghttp2_select_alpn(const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen); + +/** + * @function + * + * Returns a pointer to a nghttp2_info struct with version information + * about the run-time library in use. The |least_version| argument + * can be set to a 24 bit numerical value for the least accepted + * version number and if the condition is not met, this function will + * return a ``NULL``. Pass in 0 to skip the version checking. + */ +NGHTTP2_EXTERN nghttp2_info *nghttp2_version(int least_version); + +/** + * @function + * + * Returns nonzero if the :type:`nghttp2_error` library error code + * |lib_error| is fatal. + */ +NGHTTP2_EXTERN int nghttp2_is_fatal(int lib_error_code); + +/** + * @function + * + * Returns nonzero if HTTP header field name |name| of length |len| is + * valid according to http://tools.ietf.org/html/rfc7230#section-3.2 + * + * Because this is a header field name in HTTP2, the upper cased alphabet + * is treated as error. + */ +NGHTTP2_EXTERN int nghttp2_check_header_name(const uint8_t *name, size_t len); + +/** + * @function + * + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + * + * This function is considered obsolete, and application should + * consider to use `nghttp2_check_header_value_rfc9113()` instead. + */ +NGHTTP2_EXTERN int nghttp2_check_header_value(const uint8_t *value, size_t len); + +/** + * @function + * + * Returns nonzero if HTTP header field value |value| of length |len| + * is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2, plus + * https://datatracker.ietf.org/doc/html/rfc9113#section-8.2.1 + */ +NGHTTP2_EXTERN int nghttp2_check_header_value_rfc9113(const uint8_t *value, + size_t len); + +/** + * @function + * + * Returns nonzero if the |value| which is supposed to be the value of + * the :method header field is valid according to + * https://datatracker.ietf.org/doc/html/rfc7231#section-4 and + * https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6 + */ +NGHTTP2_EXTERN int nghttp2_check_method(const uint8_t *value, size_t len); + +/** + * @function + * + * Returns nonzero if the |value| which is supposed to be the value of + * the :path header field is valid according to + * https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.3 + * + * |value| is valid if it merely consists of the allowed characters. + * In particular, it does not check whether |value| follows the syntax + * of path. The allowed characters are all characters valid by + * `nghttp2_check_header_value` minus SPC and HT. + */ +NGHTTP2_EXTERN int nghttp2_check_path(const uint8_t *value, size_t len); + +/** + * @function + * + * Returns nonzero if the |value| which is supposed to be the value of the + * :authority or host header field is valid according to + * https://tools.ietf.org/html/rfc3986#section-3.2 + * + * Note that :authority and host field values are not authority. They + * do not include userinfo in RFC 3986, see + * https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2, that + * is, it does not include '@'. This function treats '@' as a valid + * character. + * + * |value| is valid if it merely consists of the allowed characters. + * In particular, it does not check whether |value| follows the syntax + * of authority. + */ +NGHTTP2_EXTERN int nghttp2_check_authority(const uint8_t *value, size_t len); + +/* HPACK API */ + +struct nghttp2_hd_deflater; + +/** + * @struct + * + * HPACK deflater object. + */ +typedef struct nghttp2_hd_deflater nghttp2_hd_deflater; + +/** + * @function + * + * Initializes |*deflater_ptr| for deflating name/values pairs. + * + * The |max_deflate_dynamic_table_size| is the upper bound of header + * table size the deflater will use. + * + * If this function fails, |*deflater_ptr| is left untouched. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_new(nghttp2_hd_deflater **deflater_ptr, + size_t max_deflate_dynamic_table_size); + +/** + * @function + * + * Like `nghttp2_hd_deflate_new()`, but with additional custom memory + * allocator specified in the |mem|. + * + * The |mem| can be ``NULL`` and the call is equivalent to + * `nghttp2_hd_deflate_new()`. + * + * This function does not take ownership |mem|. The application is + * responsible for freeing |mem|. + * + * The library code does not refer to |mem| pointer after this + * function returns, so the application can safely free it. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_new2(nghttp2_hd_deflater **deflater_ptr, + size_t max_deflate_dynamic_table_size, + nghttp2_mem *mem); + +/** + * @function + * + * Deallocates any resources allocated for |deflater|. + */ +NGHTTP2_EXTERN void nghttp2_hd_deflate_del(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Changes header table size of the |deflater| to + * |settings_max_dynamic_table_size| bytes. This may trigger eviction + * in the dynamic table. + * + * The |settings_max_dynamic_table_size| should be the value received + * in SETTINGS_HEADER_TABLE_SIZE. + * + * The deflater never uses more memory than + * ``max_deflate_dynamic_table_size`` bytes specified in + * `nghttp2_hd_deflate_new()`. Therefore, if + * |settings_max_dynamic_table_size| > + * ``max_deflate_dynamic_table_size``, resulting maximum table size + * becomes ``max_deflate_dynamic_table_size``. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + */ +NGHTTP2_EXTERN int +nghttp2_hd_deflate_change_table_size(nghttp2_hd_deflater *deflater, + size_t settings_max_dynamic_table_size); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_hd_deflate_hd2()` instead. + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |buf| of length |buflen|. + * + * If |buf| is not large enough to store the deflated header block, + * this function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |buf| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nva, + size_t nvlen); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |buf| of length |buflen|. + * + * If |buf| is not large enough to store the deflated header block, + * this function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |buf| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN nghttp2_ssize +nghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater, uint8_t *buf, + size_t buflen, const nghttp2_nv *nva, size_t nvlen); + +#ifndef NGHTTP2_NO_SSIZE_T +/** + * @function + * + * .. warning:: + * + * Deprecated. Use `nghttp2_hd_deflate_hd_vec2()` instead. + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |veclen| size of buf vector |vec|. The each size of buffer + * must be set in len field of :type:`nghttp2_vec`. If and only if + * one chunk is filled up completely, next chunk will be used. If + * |vec| is not large enough to store the deflated header block, this + * function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |vec| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, + const nghttp2_vec *vec, + size_t veclen, + const nghttp2_nv *nva, + size_t nvlen); + +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Deflates the |nva|, which has the |nvlen| name/value pairs, into + * the |veclen| size of buf vector |vec|. The each size of buffer + * must be set in len field of :type:`nghttp2_vec`. If and only if + * one chunk is filled up completely, next chunk will be used. If + * |vec| is not large enough to store the deflated header block, this + * function fails with + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE`. The caller + * should use `nghttp2_hd_deflate_bound()` to know the upper bound of + * buffer size required to deflate given header name/value pairs. + * + * Once this function fails, subsequent call of this function always + * returns :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP`. + * + * After this function returns, it is safe to delete the |nva|. + * + * This function returns the number of bytes written to |vec| if it + * succeeds, or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Deflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_INSUFF_BUFSIZE` + * The provided |buflen| size is too small to hold the output. + */ +NGHTTP2_EXTERN nghttp2_ssize nghttp2_hd_deflate_hd_vec2( + nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, + const nghttp2_nv *nva, size_t nvlen); + +/** + * @function + * + * Returns an upper bound on the compressed size after deflation of + * |nva| of length |nvlen|. + */ +NGHTTP2_EXTERN size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, + const nghttp2_nv *nva, + size_t nvlen); + +/** + * @function + * + * Returns the number of entries that header table of |deflater| + * contains. This is the sum of the number of static table and + * dynamic table, so the return value is at least 61. + */ +NGHTTP2_EXTERN +size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater); + +/** + * @function + * + * Returns the table entry denoted by |idx| from header table of + * |deflater|. The |idx| is 1-based, and idx=1 returns first entry of + * static table. idx=62 returns first entry of dynamic table if it + * exists. Specifying idx=0 is error, and this function returns NULL. + * If |idx| is strictly greater than the number of entries the tables * contain, this function returns NULL. */ NGHTTP2_EXTERN @@ -1457,7 +6398,7 @@ typedef struct nghttp2_hd_inflater nghttp2_hd_inflater; * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. */ NGHTTP2_EXTERN int nghttp2_hd_inflate_new(nghttp2_hd_inflater **inflater_ptr); @@ -1499,16 +6440,16 @@ NGHTTP2_EXTERN void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater); * This function must not be called while header block is being * inflated. In other words, this function must be called after * initialization of |inflater|, but before calling - * `nghttp2_hd_inflate_hd2()`, or after + * `nghttp2_hd_inflate_hd3()`, or after * `nghttp2_hd_inflate_end_headers()`. Otherwise, * `NGHTTP2_ERR_INVALID_STATE` was returned. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. - * :enum:`NGHTTP2_ERR_INVALID_STATE` + * :enum:`nghttp2_error.NGHTTP2_ERR_INVALID_STATE` * The function is called while header block is being inflated. * Probably, application missed to call * `nghttp2_hd_inflate_end_headers()`. @@ -1537,6 +6478,7 @@ typedef enum { NGHTTP2_HD_INFLATE_EMIT = 0x02 } nghttp2_hd_inflate_flag; +#ifndef NGHTTP2_NO_SSIZE_T /** * @function * @@ -1546,7 +6488,8 @@ typedef enum { * * Inflates name/value block stored in |in| with length |inlen|. This * function performs decompression. For each successful emission of - * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in + * header name/value pair, + * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in * |*inflate_flags| and name/value pair is assigned to the |nv_out| * and the function returns. The caller must not free the members of * |nv_out|. @@ -1569,11 +6512,11 @@ typedef enum { * This function returns the number of bytes processed if it succeeds, * or one of the following negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Inflation process has failed. - * :enum:`NGHTTP2_ERR_BUFFER_ERROR` + * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` * The header field name or value is too large. * * Example follows:: @@ -1623,12 +6566,20 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, int *inflate_flags, uint8_t *in, size_t inlen, int in_final); +#endif /* NGHTTP2_NO_SSIZE_T */ + +#ifndef NGHTTP2_NO_SSIZE_T /** * @function * + * .. warning:: + * + * Deprecated. Use `nghttp2_hd_inflate_hd3()` instead. + * * Inflates name/value block stored in |in| with length |inlen|. This * function performs decompression. For each successful emission of - * header name/value pair, :enum:`NGHTTP2_HD_INFLATE_EMIT` is set in + * header name/value pair, + * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in * |*inflate_flags| and name/value pair is assigned to the |nv_out| * and the function returns. The caller must not free the members of * |nv_out|. @@ -1644,8 +6595,9 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, * for the next header block input. * * In other words, if |in_final| is nonzero, and this function returns - * |inlen|, you can assert that :enum:`NGHTTP2_HD_INFLATE_FINAL` is - * set in |*inflate_flags|. + * |inlen|, you can assert that + * :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in + * |*inflate_flags|. * * The caller can feed complete compressed header block. It also can * feed it in several chunks. The caller must set |in_final| to @@ -1655,11 +6607,11 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, * This function returns the number of bytes processed if it succeeds, * or one of the following negative error codes: * - * :enum:`NGHTTP2_ERR_NOMEM` + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` * Out of memory. - * :enum:`NGHTTP2_ERR_HEADER_COMP` + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` * Inflation process has failed. - * :enum:`NGHTTP2_ERR_BUFFER_ERROR` + * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` * The header field name or value is too large. * * Example follows:: @@ -1710,6 +6662,95 @@ NGHTTP2_EXTERN ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, const uint8_t *in, size_t inlen, int in_final); +#endif /* NGHTTP2_NO_SSIZE_T */ + +/** + * @function + * + * Inflates name/value block stored in |in| with length |inlen|. This + * function performs decompression. For each successful emission of + * header name/value pair, + * :enum:`nghttp2_hd_inflate_flag.NGHTTP2_HD_INFLATE_EMIT` is set in + * |*inflate_flags| and name/value pair is assigned to the |nv_out| + * and the function returns. The caller must not free the members of + * |nv_out|. + * + * The |nv_out| may include pointers to the memory region in the |in|. + * The caller must retain the |in| while the |nv_out| is used. + * + * The application should call this function repeatedly until the + * ``(*inflate_flags) & NGHTTP2_HD_INFLATE_FINAL`` is nonzero and + * return value is non-negative. If that happens, all given input + * data (|inlen| bytes) are processed successfully. Then the + * application must call `nghttp2_hd_inflate_end_headers()` to prepare + * for the next header block input. + * + * In other words, if |in_final| is nonzero, and this function returns + * |inlen|, you can assert that + * :enum:`nghttp2_hd_inflate_final.NGHTTP2_HD_INFLATE_FINAL` is set in + * |*inflate_flags|. + * + * The caller can feed complete compressed header block. It also can + * feed it in several chunks. The caller must set |in_final| to + * nonzero if the given input is the last block of the compressed + * header. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * + * :enum:`nghttp2_error.NGHTTP2_ERR_NOMEM` + * Out of memory. + * :enum:`nghttp2_error.NGHTTP2_ERR_HEADER_COMP` + * Inflation process has failed. + * :enum:`nghttp2_error.NGHTTP2_ERR_BUFFER_ERROR` + * The header field name or value is too large. + * + * Example follows:: + * + * int inflate_header_block(nghttp2_hd_inflater *hd_inflater, + * uint8_t *in, size_t inlen, int final) + * { + * nghttp2_ssize rv; + * + * for(;;) { + * nghttp2_nv nv; + * int inflate_flags = 0; + * + * rv = nghttp2_hd_inflate_hd3(hd_inflater, &nv, &inflate_flags, + * in, inlen, final); + * + * if(rv < 0) { + * fprintf(stderr, "inflate failed with error code %td", rv); + * return -1; + * } + * + * in += rv; + * inlen -= rv; + * + * if(inflate_flags & NGHTTP2_HD_INFLATE_EMIT) { + * fwrite(nv.name, nv.namelen, 1, stderr); + * fprintf(stderr, ": "); + * fwrite(nv.value, nv.valuelen, 1, stderr); + * fprintf(stderr, "\n"); + * } + * if(inflate_flags & NGHTTP2_HD_INFLATE_FINAL) { + * nghttp2_hd_inflate_end_headers(hd_inflater); + * break; + * } + * if((inflate_flags & NGHTTP2_HD_INFLATE_EMIT) == 0 && + * inlen == 0) { + * break; + * } + * } + * + * return 0; + * } + * + */ +NGHTTP2_EXTERN nghttp2_ssize nghttp2_hd_inflate_hd3( + nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, int in_final); + /** * @function * @@ -1763,6 +6804,32 @@ NGHTTP2_EXTERN size_t nghttp2_hd_inflate_get_max_dynamic_table_size(nghttp2_hd_inflater *inflater); +struct nghttp2_stream; + +/** + * @struct + * + * The structure to represent HTTP/2 stream. The details of this + * structure are intentionally hidden from the public API. + */ +typedef struct nghttp2_stream nghttp2_stream; + +/** + * @function + * + * Returns pointer to :type:`nghttp2_stream` object denoted by + * |stream_id|. If stream was not found, returns NULL. + * + * Returns imaginary root stream (see + * `nghttp2_session_get_root_stream()`) if 0 is given in |stream_id|. + * + * Unless |stream_id| == 0, the returned pointer is valid until next + * call of `nghttp2_session_send()`, `nghttp2_session_mem_send2()`, + * `nghttp2_session_recv()`, and `nghttp2_session_mem_recv2()`. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_session_find_stream(nghttp2_session *session, int32_t stream_id); + /** * @enum * @@ -1799,6 +6866,128 @@ typedef enum { NGHTTP2_STREAM_STATE_CLOSED } nghttp2_stream_proto_state; +/** + * @function + * + * Returns state of |stream|. The root stream retrieved by + * `nghttp2_session_get_root_stream()` will have stream state + * :enum:`nghttp2_stream_proto_state.NGHTTP2_STREAM_STATE_IDLE`. + */ +NGHTTP2_EXTERN nghttp2_stream_proto_state +nghttp2_stream_get_state(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. + * + * Returns root of dependency tree, which is imaginary stream with + * stream ID 0. The returned pointer is valid until |session| is + * freed by `nghttp2_session_del()`. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_session_get_root_stream(nghttp2_session *session); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return NULL. + * + * Returns the parent stream of |stream| in dependency tree. Returns + * NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_parent(nghttp2_stream *stream); + +NGHTTP2_EXTERN int32_t nghttp2_stream_get_stream_id(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return NULL. + * + * Returns the next sibling stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_next_sibling(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return NULL. + * + * Returns the previous sibling stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_previous_sibling(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return NULL. + * + * Returns the first child stream of |stream| in dependency tree. + * Returns NULL if there is no such stream. + */ +NGHTTP2_EXTERN nghttp2_stream * +nghttp2_stream_get_first_child(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return + * :macro:`NGHTTP2_DEFAULT_WEIGHT`. + * + * Returns dependency weight to the parent stream of |stream|. + */ +NGHTTP2_EXTERN int32_t nghttp2_stream_get_weight(nghttp2_stream *stream); + +/** + * @function + * + * .. warning:: + * + * Deprecated. :rfc:`7540` priorities are deprecated by + * :rfc:`9113`. Consider migrating to :rfc:`9218` extensible + * prioritization scheme. In the future release after the end of + * 2024, this function will always return 0. + * + * Returns the sum of the weight for |stream|'s children. + */ +NGHTTP2_EXTERN int32_t +nghttp2_stream_get_sum_dependency_weight(nghttp2_stream *stream); + /** * @functypedef * @@ -1833,7 +7022,7 @@ typedef void (*nghttp2_debug_vprintf_callback)(const char *format, * this is important. */ NGHTTP2_EXTERN void nghttp2_set_debug_vprintf_callback( - nghttp2_debug_vprintf_callback debug_vprintf_callback); + nghttp2_debug_vprintf_callback debug_vprintf_callback); #ifdef __cplusplus } diff --git a/thirdparty/nghttp2/nghttp2_buf.c b/thirdparty/nghttp2/nghttp2_buf.c index 9d4bbead38f..3cdfe5be564 100644 --- a/thirdparty/nghttp2/nghttp2_buf.c +++ b/thirdparty/nghttp2/nghttp2_buf.c @@ -58,7 +58,7 @@ int nghttp2_buf_reserve(nghttp2_buf *buf, size_t new_cap, nghttp2_mem *mem) { return 0; } - new_cap = nghttp2_max(new_cap, cap * 2); + new_cap = nghttp2_max_size(new_cap, cap * 2); ptr = nghttp2_mem_realloc(mem, buf->begin, new_cap); if (ptr == NULL) { @@ -79,8 +79,10 @@ void nghttp2_buf_reset(nghttp2_buf *buf) { } void nghttp2_buf_wrap_init(nghttp2_buf *buf, uint8_t *begin, size_t len) { - buf->begin = buf->pos = buf->last = buf->mark = begin; - buf->end = begin + len; + buf->begin = buf->pos = buf->last = buf->mark = buf->end = begin; + if (len) { + buf->end += len; + } } static int buf_chain_new(nghttp2_buf_chain **chain, size_t chunk_length, @@ -338,7 +340,7 @@ int nghttp2_bufs_add(nghttp2_bufs *bufs, const void *data, size_t len) { while (len) { buf = &bufs->cur->buf; - nwrite = nghttp2_min(nghttp2_buf_avail(buf), len); + nwrite = nghttp2_min_size(nghttp2_buf_avail(buf), len); if (nwrite == 0) { rv = bufs_alloc_chain(bufs); if (rv != 0) { @@ -425,7 +427,7 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b) { return 0; } -ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { +nghttp2_ssize nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { size_t len; nghttp2_buf_chain *chain; nghttp2_buf *buf; @@ -457,7 +459,7 @@ ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out) { *out = res; - return (ssize_t)len; + return (nghttp2_ssize)len; } size_t nghttp2_bufs_remove_copy(nghttp2_bufs *bufs, uint8_t *out) { diff --git a/thirdparty/nghttp2/nghttp2_buf.h b/thirdparty/nghttp2/nghttp2_buf.h index 9a21004ad38..3603a5a2e15 100644 --- a/thirdparty/nghttp2/nghttp2_buf.h +++ b/thirdparty/nghttp2/nghttp2_buf.h @@ -94,7 +94,7 @@ void nghttp2_buf_free(nghttp2_buf *buf, nghttp2_mem *mem); * |new_cap|. If extensions took place, buffer pointers in |buf| will * change. * - * This function returns 0 if it succeeds, or one of the followings + * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGHTTP2_ERR_NOMEM @@ -344,7 +344,7 @@ int nghttp2_bufs_orb_hold(nghttp2_bufs *bufs, uint8_t b); * NGHTTP2_ERR_NOMEM * Out of memory */ -ssize_t nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); +nghttp2_ssize nghttp2_bufs_remove(nghttp2_bufs *bufs, uint8_t **out); /* * Copies all data stored in |bufs| to |out|. This function assumes @@ -393,7 +393,7 @@ int nghttp2_bufs_advance(nghttp2_bufs *bufs); void nghttp2_bufs_seek_last_present(nghttp2_bufs *bufs); /* - * Returns nonzero if bufs->cur->next is not emtpy. + * Returns nonzero if bufs->cur->next is not empty. */ int nghttp2_bufs_next_present(nghttp2_bufs *bufs); diff --git a/thirdparty/nghttp2/nghttp2_hd.c b/thirdparty/nghttp2/nghttp2_hd.c index 8d98abe0838..e26ec93e548 100644 --- a/thirdparty/nghttp2/nghttp2_hd.c +++ b/thirdparty/nghttp2/nghttp2_hd.c @@ -32,77 +32,78 @@ #define MAKE_STATIC_ENT(N, V, T, H) \ { \ {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ - {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ - {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \ - T, H \ + {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, \ + {(uint8_t *)(N), (uint8_t *)(V), sizeof((N)) - 1, sizeof((V)) - 1, 0}, \ + T, \ + H, \ } /* Generated by mkstatictbl.py */ /* 3rd parameter is nghttp2_token value for header field name. We use first enum value if same header names are repeated (e.g., :status). */ -static nghttp2_hd_static_entry static_table[] = { - MAKE_STATIC_ENT(":authority", "", 0, 3153725150u), - MAKE_STATIC_ENT(":method", "GET", 1, 695666056u), - MAKE_STATIC_ENT(":method", "POST", 1, 695666056u), - MAKE_STATIC_ENT(":path", "/", 3, 3292848686u), - MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u), - MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u), - MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u), - MAKE_STATIC_ENT(":status", "200", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "204", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "206", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "304", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "400", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "404", 7, 4000288983u), - MAKE_STATIC_ENT(":status", "500", 7, 4000288983u), - MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u), - MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u), - MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u), - MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u), - MAKE_STATIC_ENT("accept", "", 18, 136609321u), - MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u), - MAKE_STATIC_ENT("age", "", 20, 742476188u), - MAKE_STATIC_ENT("allow", "", 21, 2930878514u), - MAKE_STATIC_ENT("authorization", "", 22, 2436257726u), - MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u), - MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u), - MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u), - MAKE_STATIC_ENT("content-language", "", 26, 24973587u), - MAKE_STATIC_ENT("content-length", "", 27, 1308181789u), - MAKE_STATIC_ENT("content-location", "", 28, 2302364718u), - MAKE_STATIC_ENT("content-range", "", 29, 3555523146u), - MAKE_STATIC_ENT("content-type", "", 30, 4244048277u), - MAKE_STATIC_ENT("cookie", "", 31, 2007449791u), - MAKE_STATIC_ENT("date", "", 32, 3564297305u), - MAKE_STATIC_ENT("etag", "", 33, 113792960u), - MAKE_STATIC_ENT("expect", "", 34, 2530896728u), - MAKE_STATIC_ENT("expires", "", 35, 1049544579u), - MAKE_STATIC_ENT("from", "", 36, 2513272949u), - MAKE_STATIC_ENT("host", "", 37, 2952701295u), - MAKE_STATIC_ENT("if-match", "", 38, 3597694698u), - MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u), - MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u), - MAKE_STATIC_ENT("if-range", "", 41, 2340978238u), - MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u), - MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u), - MAKE_STATIC_ENT("link", "", 44, 232457833u), - MAKE_STATIC_ENT("location", "", 45, 200649126u), - MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u), - MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u), - MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u), - MAKE_STATIC_ENT("range", "", 49, 4208725202u), - MAKE_STATIC_ENT("referer", "", 50, 3969579366u), - MAKE_STATIC_ENT("refresh", "", 51, 3572655668u), - MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u), - MAKE_STATIC_ENT("server", "", 53, 1085029842u), - MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u), - MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u), - MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u), - MAKE_STATIC_ENT("user-agent", "", 57, 606444526u), - MAKE_STATIC_ENT("vary", "", 58, 1085005381u), - MAKE_STATIC_ENT("via", "", 59, 1762798611u), - MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u), +static const nghttp2_hd_static_entry static_table[] = { + MAKE_STATIC_ENT(":authority", "", 0, 3153725150u), + MAKE_STATIC_ENT(":method", "GET", 1, 695666056u), + MAKE_STATIC_ENT(":method", "POST", 1, 695666056u), + MAKE_STATIC_ENT(":path", "/", 3, 3292848686u), + MAKE_STATIC_ENT(":path", "/index.html", 3, 3292848686u), + MAKE_STATIC_ENT(":scheme", "http", 5, 2510477674u), + MAKE_STATIC_ENT(":scheme", "https", 5, 2510477674u), + MAKE_STATIC_ENT(":status", "200", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "204", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "206", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "304", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "400", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "404", 7, 4000288983u), + MAKE_STATIC_ENT(":status", "500", 7, 4000288983u), + MAKE_STATIC_ENT("accept-charset", "", 14, 3664010344u), + MAKE_STATIC_ENT("accept-encoding", "gzip, deflate", 15, 3379649177u), + MAKE_STATIC_ENT("accept-language", "", 16, 1979086614u), + MAKE_STATIC_ENT("accept-ranges", "", 17, 1713753958u), + MAKE_STATIC_ENT("accept", "", 18, 136609321u), + MAKE_STATIC_ENT("access-control-allow-origin", "", 19, 2710797292u), + MAKE_STATIC_ENT("age", "", 20, 742476188u), + MAKE_STATIC_ENT("allow", "", 21, 2930878514u), + MAKE_STATIC_ENT("authorization", "", 22, 2436257726u), + MAKE_STATIC_ENT("cache-control", "", 23, 1355326669u), + MAKE_STATIC_ENT("content-disposition", "", 24, 3889184348u), + MAKE_STATIC_ENT("content-encoding", "", 25, 65203592u), + MAKE_STATIC_ENT("content-language", "", 26, 24973587u), + MAKE_STATIC_ENT("content-length", "", 27, 1308181789u), + MAKE_STATIC_ENT("content-location", "", 28, 2302364718u), + MAKE_STATIC_ENT("content-range", "", 29, 3555523146u), + MAKE_STATIC_ENT("content-type", "", 30, 4244048277u), + MAKE_STATIC_ENT("cookie", "", 31, 2007449791u), + MAKE_STATIC_ENT("date", "", 32, 3564297305u), + MAKE_STATIC_ENT("etag", "", 33, 113792960u), + MAKE_STATIC_ENT("expect", "", 34, 2530896728u), + MAKE_STATIC_ENT("expires", "", 35, 1049544579u), + MAKE_STATIC_ENT("from", "", 36, 2513272949u), + MAKE_STATIC_ENT("host", "", 37, 2952701295u), + MAKE_STATIC_ENT("if-match", "", 38, 3597694698u), + MAKE_STATIC_ENT("if-modified-since", "", 39, 2213050793u), + MAKE_STATIC_ENT("if-none-match", "", 40, 2536202615u), + MAKE_STATIC_ENT("if-range", "", 41, 2340978238u), + MAKE_STATIC_ENT("if-unmodified-since", "", 42, 3794814858u), + MAKE_STATIC_ENT("last-modified", "", 43, 3226950251u), + MAKE_STATIC_ENT("link", "", 44, 232457833u), + MAKE_STATIC_ENT("location", "", 45, 200649126u), + MAKE_STATIC_ENT("max-forwards", "", 46, 1826162134u), + MAKE_STATIC_ENT("proxy-authenticate", "", 47, 2709445359u), + MAKE_STATIC_ENT("proxy-authorization", "", 48, 2686392507u), + MAKE_STATIC_ENT("range", "", 49, 4208725202u), + MAKE_STATIC_ENT("referer", "", 50, 3969579366u), + MAKE_STATIC_ENT("refresh", "", 51, 3572655668u), + MAKE_STATIC_ENT("retry-after", "", 52, 3336180598u), + MAKE_STATIC_ENT("server", "", 53, 1085029842u), + MAKE_STATIC_ENT("set-cookie", "", 54, 1848371000u), + MAKE_STATIC_ENT("strict-transport-security", "", 55, 4138147361u), + MAKE_STATIC_ENT("transfer-encoding", "", 56, 3719590988u), + MAKE_STATIC_ENT("user-agent", "", 57, 606444526u), + MAKE_STATIC_ENT("vary", "", 58, 1085005381u), + MAKE_STATIC_ENT("via", "", 59, 1762798611u), + MAKE_STATIC_ENT("www-authenticate", "", 60, 779865858u), }; static int memeq(const void *s1, const void *s2, size_t n) { @@ -265,6 +266,20 @@ static int32_t lookup_token(const uint8_t *name, size_t namelen) { return NGHTTP2_TOKEN_LOCATION; } break; + case 'y': + if (memeq("priorit", name, 7)) { + return NGHTTP2_TOKEN_PRIORITY; + } + break; + } + break; + case 9: + switch (name[8]) { + case 'l': + if (memeq(":protoco", name, 8)) { + return NGHTTP2_TOKEN__PROTOCOL; + } + break; } break; case 10: @@ -658,9 +673,9 @@ static int hd_context_init(nghttp2_hd_context *context, nghttp2_mem *mem) { context->mem = mem; context->bad = 0; context->hd_table_bufsize_max = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; - rv = hd_ringbuf_init(&context->hd_table, context->hd_table_bufsize_max / - NGHTTP2_HD_ENTRY_OVERHEAD, - mem); + rv = hd_ringbuf_init( + &context->hd_table, + context->hd_table_bufsize_max / NGHTTP2_HD_ENTRY_OVERHEAD, mem); if (rv != 0) { return rv; } @@ -677,7 +692,7 @@ static void hd_context_free(nghttp2_hd_context *context) { int nghttp2_hd_deflate_init(nghttp2_hd_deflater *deflater, nghttp2_mem *mem) { return nghttp2_hd_deflate_init2( - deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); + deflater, NGHTTP2_HD_DEFAULT_MAX_DEFLATE_BUFFER_SIZE, mem); } int nghttp2_hd_deflate_init2(nghttp2_hd_deflater *deflater, @@ -832,9 +847,10 @@ static size_t encode_length(uint8_t *buf, size_t n, size_t prefix) { * in the next call will be stored in |*shift_ptr|) and returns number * of bytes processed, or returns -1, indicating decoding error. */ -static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, - uint32_t initial, size_t shift, const uint8_t *in, - const uint8_t *last, size_t prefix) { +static nghttp2_ssize decode_length(uint32_t *res, size_t *shift_ptr, int *fin, + uint32_t initial, size_t shift, + const uint8_t *in, const uint8_t *last, + size_t prefix) { uint32_t k = (uint8_t)((1 << prefix) - 1); uint32_t n = initial; const uint8_t *start = in; @@ -853,13 +869,18 @@ static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, if (++in == last) { *res = n; - return (ssize_t)(in - start); + return (nghttp2_ssize)(in - start); } } for (; in != last; ++in, shift += 7) { uint32_t add = *in & 0x7f; + if (shift >= 32) { + DEBUGF("inflate: shift exponent overflow\n"); + return -1; + } + if ((UINT32_MAX >> shift) < add) { DEBUGF("inflate: integer overflow on shift\n"); return -1; @@ -883,12 +904,12 @@ static ssize_t decode_length(uint32_t *res, size_t *shift_ptr, int *fin, if (in == last) { *res = n; - return (ssize_t)(in - start); + return (nghttp2_ssize)(in - start); } *res = n; *fin = 1; - return (ssize_t)(in + 1 - start); + return (nghttp2_ssize)(in + 1 - start); } static int emit_table_size(nghttp2_bufs *bufs, size_t table_size) { @@ -999,7 +1020,7 @@ static uint8_t pack_first_byte(int indexing_mode) { case NGHTTP2_HD_NEVER_INDEXING: return 0x10u; default: - abort(); + assert(0); } /* This is required to compile with android NDK r10d + --enable-werror */ @@ -1053,8 +1074,8 @@ static int emit_newname_block(nghttp2_bufs *bufs, const nghttp2_nv *nv, int rv; DEBUGF( - "deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\n", - nv->namelen, nv->valuelen, indexing_mode); + "deflatehd: emit newname namelen=%zu, valuelen=%zu, indexing_mode=%d\n", + nv->namelen, nv->valuelen, indexing_mode); rv = nghttp2_bufs_addb(bufs, pack_first_byte(indexing_mode)); if (rv != 0) { @@ -1087,12 +1108,11 @@ static int add_hd_table_incremental(nghttp2_hd_context *context, while (context->hd_table_bufsize + room > context->hd_table_bufsize_max && context->hd_table.len > 0) { - size_t idx = context->hd_table.len - 1; nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); context->hd_table_bufsize -= - entry_room(ent->nv.name->len, ent->nv.value->len); + entry_room(ent->nv.name->len, ent->nv.value->len); DEBUGF("hpack: remove item from header table: %s: %s\n", (char *)ent->nv.name->base, (char *)ent->nv.value->base); @@ -1141,7 +1161,7 @@ static int add_hd_table_incremental(nghttp2_hd_context *context, } typedef struct { - ssize_t index; + nghttp2_ssize index; /* Nonzero if both name and value are matched. */ int name_value_match; } search_result; @@ -1150,7 +1170,7 @@ static search_result search_static_table(const nghttp2_nv *nv, int32_t token, int name_only) { search_result res = {token, 0}; int i; - nghttp2_hd_static_entry *ent; + const nghttp2_hd_static_entry *ent; if (name_only) { return res; @@ -1175,7 +1195,7 @@ static search_result search_hd_table(nghttp2_hd_context *context, int indexing_mode, nghttp2_hd_map *map, uint32_t hash) { search_result res = {-1, 0}; - nghttp2_hd_entry *ent; + const nghttp2_hd_entry *ent; int exact_match; int name_only = indexing_mode == NGHTTP2_HD_NEVER_INDEXING; @@ -1190,8 +1210,8 @@ static search_result search_hd_table(nghttp2_hd_context *context, return res; } - res.index = - (ssize_t)(context->next_seq - 1 - ent->seq + NGHTTP2_STATIC_TABLE_LENGTH); + res.index = (nghttp2_ssize)(context->next_seq - 1 - ent->seq + + NGHTTP2_STATIC_TABLE_LENGTH); res.name_value_match = exact_match; return res; @@ -1208,7 +1228,7 @@ static void hd_context_shrink_table_size(nghttp2_hd_context *context, size_t idx = context->hd_table.len - 1; nghttp2_hd_entry *ent = hd_ringbuf_get(&context->hd_table, idx); context->hd_table_bufsize -= - entry_room(ent->nv.name->len, ent->nv.value->len); + entry_room(ent->nv.name->len, ent->nv.value->len); hd_ringbuf_pop_back(&context->hd_table); if (map) { hd_map_remove(map, ent); @@ -1220,14 +1240,14 @@ static void hd_context_shrink_table_size(nghttp2_hd_context *context, } int nghttp2_hd_deflate_change_table_size( - nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) { - size_t next_bufsize = nghttp2_min(settings_max_dynamic_table_size, - deflater->deflate_hd_table_bufsize_max); + nghttp2_hd_deflater *deflater, size_t settings_max_dynamic_table_size) { + size_t next_bufsize = nghttp2_min_size( + settings_max_dynamic_table_size, deflater->deflate_hd_table_bufsize_max); deflater->ctx.hd_table_bufsize_max = next_bufsize; deflater->min_hd_table_bufsize_max = - nghttp2_min(deflater->min_hd_table_bufsize_max, next_bufsize); + nghttp2_min_size(deflater->min_hd_table_bufsize_max, next_bufsize); deflater->notify_table_size_change = 1; @@ -1236,7 +1256,7 @@ int nghttp2_hd_deflate_change_table_size( } int nghttp2_hd_inflate_change_table_size( - nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) { + nghttp2_hd_inflater *inflater, size_t settings_max_dynamic_table_size) { switch (inflater->state) { case NGHTTP2_HD_STATE_EXPECT_TABLE_SIZE: case NGHTTP2_HD_STATE_INFLATE_START: @@ -1245,6 +1265,8 @@ int nghttp2_hd_inflate_change_table_size( return NGHTTP2_ERR_INVALID_STATE; } + inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; + /* It seems that encoder is not required to send dynamic table size update if the table size is not changed after applying SETTINGS_HEADER_TABLE_SIZE. RFC 7541 is ambiguous here, but this @@ -1257,13 +1279,12 @@ int nghttp2_hd_inflate_change_table_size( /* Remember minimum value, and validate that encoder sends the value less than or equal to this. */ inflater->min_hd_table_bufsize_max = settings_max_dynamic_table_size; - } - inflater->settings_hd_table_bufsize_max = settings_max_dynamic_table_size; + inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; - inflater->ctx.hd_table_bufsize_max = settings_max_dynamic_table_size; + hd_context_shrink_table_size(&inflater->ctx, NULL); + } - hd_context_shrink_table_size(&inflater->ctx, NULL); return 0; } @@ -1278,10 +1299,11 @@ nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t idx) { assert(INDEX_RANGE_VALID(context, idx)); if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { return hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) - ->nv; + ->nv; } else { - nghttp2_hd_static_entry *ent = &static_table[idx]; - nghttp2_hd_nv nv = {&ent->name, &ent->value, ent->token, + const nghttp2_hd_static_entry *ent = &static_table[idx]; + nghttp2_hd_nv nv = {(nghttp2_rcbuf *)&ent->name, + (nghttp2_rcbuf *)&ent->value, ent->token, NGHTTP2_NV_FLAG_NONE}; return nv; } @@ -1293,7 +1315,7 @@ static const nghttp2_nv *nghttp2_hd_table_get2(nghttp2_hd_context *context, if (idx >= NGHTTP2_STATIC_TABLE_LENGTH) { return &hd_ringbuf_get(&context->hd_table, idx - NGHTTP2_STATIC_TABLE_LENGTH) - ->cnv; + ->cnv; } return &static_table[idx].cnv; @@ -1307,7 +1329,7 @@ static int hd_deflate_decide_indexing(nghttp2_hd_deflater *deflater, token == NGHTTP2_TOKEN_IF_NONE_MATCH || token == NGHTTP2_TOKEN_LOCATION || token == NGHTTP2_TOKEN_SET_COOKIE || entry_room(nv->namelen, nv->valuelen) > - deflater->ctx.hd_table_bufsize_max * 3 / 4) { + deflater->ctx.hd_table_bufsize_max * 3 / 4) { return NGHTTP2_HD_WITHOUT_INDEXING; } @@ -1318,7 +1340,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, const nghttp2_nv *nv) { int rv; search_result res; - ssize_t idx; + nghttp2_ssize idx; int indexing_mode; int32_t token; nghttp2_mem *mem; @@ -1340,12 +1362,11 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, entropy secret data (e.g., id/password). Also cookie header field with less than 20 bytes value is also never indexed. This is the same criteria used in Firefox codebase. */ - indexing_mode = - token == NGHTTP2_TOKEN_AUTHORIZATION || - (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || - (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) - ? NGHTTP2_HD_NEVER_INDEXING - : hd_deflate_decide_indexing(deflater, nv, token); + indexing_mode = token == NGHTTP2_TOKEN_AUTHORIZATION || + (token == NGHTTP2_TOKEN_COOKIE && nv->valuelen < 20) || + (nv->flags & NGHTTP2_NV_FLAG_NO_INDEX) + ? NGHTTP2_HD_NEVER_INDEXING + : hd_deflate_decide_indexing(deflater, nv, token); res = search_hd_table(&deflater->ctx, nv, token, indexing_mode, &deflater->map, hash); @@ -1353,8 +1374,7 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, idx = res.index; if (res.name_value_match) { - - DEBUGF("deflatehd: name/value match index=%zd\n", idx); + DEBUGF("deflatehd: name/value match index=%td\n", idx); rv = emit_indexed_block(bufs, (size_t)idx); if (rv != 0) { @@ -1365,13 +1385,13 @@ static int deflate_nv(nghttp2_hd_deflater *deflater, nghttp2_bufs *bufs, } if (res.index != -1) { - DEBUGF("deflatehd: name match index=%zd\n", res.index); + DEBUGF("deflatehd: name match index=%td\n", res.index); } if (indexing_mode == NGHTTP2_HD_WITH_INDEXING) { nghttp2_hd_nv hd_nv; - if (idx != -1 && idx < (ssize_t)NGHTTP2_STATIC_TABLE_LENGTH) { + if (idx != -1) { hd_nv.name = nghttp2_hd_table_get(&deflater->ctx, (size_t)idx).name; nghttp2_rcbuf_incref(hd_nv.name); } else { @@ -1431,7 +1451,6 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, deflater->min_hd_table_bufsize_max = UINT32_MAX; if (deflater->ctx.hd_table_bufsize_max > min_hd_table_bufsize_max) { - rv = emit_table_size(bufs, min_hd_table_bufsize_max); if (rv != 0) { @@ -1466,6 +1485,12 @@ int nghttp2_hd_deflate_hd_bufs(nghttp2_hd_deflater *deflater, ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, size_t buflen, const nghttp2_nv *nv, size_t nvlen) { + return (ssize_t)nghttp2_hd_deflate_hd2(deflater, buf, buflen, nv, nvlen); +} + +nghttp2_ssize nghttp2_hd_deflate_hd2(nghttp2_hd_deflater *deflater, + uint8_t *buf, size_t buflen, + const nghttp2_nv *nv, size_t nvlen) { nghttp2_bufs bufs; int rv; nghttp2_mem *mem; @@ -1492,12 +1517,18 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_deflater *deflater, uint8_t *buf, return rv; } - return (ssize_t)buflen; + return (nghttp2_ssize)buflen; } ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, const nghttp2_vec *vec, size_t veclen, const nghttp2_nv *nv, size_t nvlen) { + return (ssize_t)nghttp2_hd_deflate_hd_vec2(deflater, vec, veclen, nv, nvlen); +} + +nghttp2_ssize nghttp2_hd_deflate_hd_vec2(nghttp2_hd_deflater *deflater, + const nghttp2_vec *vec, size_t veclen, + const nghttp2_nv *nv, size_t nvlen) { nghttp2_bufs bufs; int rv; nghttp2_mem *mem; @@ -1525,13 +1556,14 @@ ssize_t nghttp2_hd_deflate_hd_vec(nghttp2_hd_deflater *deflater, return rv; } - return (ssize_t)buflen; + return (nghttp2_ssize)buflen; } size_t nghttp2_hd_deflate_bound(nghttp2_hd_deflater *deflater, const nghttp2_nv *nva, size_t nvlen) { size_t n = 0; size_t i; + (void)deflater; /* Possible Maximum Header Table Size Change. Encoding (1u << 31) - 1 using 4 bit prefix requires 6 bytes. We may emit this at most @@ -1617,10 +1649,11 @@ static void hd_inflate_set_huffman_encoded(nghttp2_hd_inflater *inflater, * NGHTTP2_ERR_HEADER_COMP * Integer decoding failed */ -static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, - const uint8_t *in, const uint8_t *last, - size_t prefix, size_t maxlen) { - ssize_t rv; +static nghttp2_ssize hd_inflate_read_len(nghttp2_hd_inflater *inflater, + int *rfin, const uint8_t *in, + const uint8_t *last, size_t prefix, + size_t maxlen) { + nghttp2_ssize rv; uint32_t out; *rfin = 0; @@ -1658,10 +1691,10 @@ static ssize_t hd_inflate_read_len(nghttp2_hd_inflater *inflater, int *rfin, * NGHTTP2_ERR_HEADER_COMP * Huffman decoding failed */ -static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, - nghttp2_buf *buf, const uint8_t *in, - const uint8_t *last) { - ssize_t readlen; +static nghttp2_ssize hd_inflate_read_huff(nghttp2_hd_inflater *inflater, + nghttp2_buf *buf, const uint8_t *in, + const uint8_t *last) { + nghttp2_ssize readlen; int fin = 0; if ((size_t)(last - in) >= inflater->left) { last = in + inflater->left; @@ -1674,6 +1707,11 @@ static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, DEBUGF("inflatehd: huffman decoding failed\n"); return readlen; } + if (nghttp2_hd_huff_decode_failure_state(&inflater->huff_decode_ctx)) { + DEBUGF("inflatehd: huffman decoding failed\n"); + return NGHTTP2_ERR_HEADER_COMP; + } + inflater->left -= (size_t)readlen; return readlen; } @@ -1690,14 +1728,15 @@ static ssize_t hd_inflate_read_huff(nghttp2_hd_inflater *inflater, * NGHTTP2_ERR_HEADER_COMP * Header decompression failed */ -static ssize_t hd_inflate_read(nghttp2_hd_inflater *inflater, nghttp2_buf *buf, - const uint8_t *in, const uint8_t *last) { - size_t len = nghttp2_min((size_t)(last - in), inflater->left); +static nghttp2_ssize hd_inflate_read(nghttp2_hd_inflater *inflater, + nghttp2_buf *buf, const uint8_t *in, + const uint8_t *last) { + size_t len = nghttp2_min_size((size_t)(last - in), inflater->left); buf->last = nghttp2_cpymem(buf->last, in, len); inflater->left -= len; - return (ssize_t)len; + return (nghttp2_ssize)len; } /* @@ -1812,7 +1851,15 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, nghttp2_nv *nv_out, int *inflate_flags, const uint8_t *in, size_t inlen, int in_final) { - ssize_t rv; + return (nghttp2_ssize)nghttp2_hd_inflate_hd3(inflater, nv_out, inflate_flags, + in, inlen, in_final); +} + +nghttp2_ssize nghttp2_hd_inflate_hd3(nghttp2_hd_inflater *inflater, + nghttp2_nv *nv_out, int *inflate_flags, + const uint8_t *in, size_t inlen, + int in_final) { + nghttp2_ssize rv; nghttp2_hd_nv hd_nv; rv = nghttp2_hd_inflate_hd_nv(inflater, &hd_nv, inflate_flags, in, inlen, @@ -1835,11 +1882,11 @@ ssize_t nghttp2_hd_inflate_hd2(nghttp2_hd_inflater *inflater, return rv; } -ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, - nghttp2_hd_nv *nv_out, int *inflate_flags, - const uint8_t *in, size_t inlen, - int in_final) { - ssize_t rv = 0; +nghttp2_ssize nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out, + int *inflate_flags, const uint8_t *in, + size_t inlen, int in_final) { + nghttp2_ssize rv = 0; const uint8_t *first = in; const uint8_t *last = in + inlen; int rfin = 0; @@ -1907,9 +1954,9 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, case NGHTTP2_HD_STATE_READ_TABLE_SIZE: rfin = 0; rv = hd_inflate_read_len( - inflater, &rfin, in, last, 5, - nghttp2_min(inflater->min_hd_table_bufsize_max, - inflater->settings_hd_table_bufsize_max)); + inflater, &rfin, in, last, 5, + nghttp2_min_size(inflater->min_hd_table_bufsize_max, + inflater->settings_hd_table_bufsize_max)); if (rv < 0) { goto fail; } @@ -1961,7 +2008,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_OPCODE; *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); } else { inflater->index = inflater->left; --inflater->index; @@ -1996,8 +2043,8 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAMEHUFF; - rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1, - mem); + rv = + nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left * 2 + 1, mem); } else { inflater->state = NGHTTP2_HD_STATE_NEWNAME_READ_NAME; rv = nghttp2_rcbuf_new(&inflater->namercbuf, inflater->left + 1, mem); @@ -2019,7 +2066,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, in += rv; - DEBUGF("inflatehd: %zd bytes read\n", rv); + DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); @@ -2041,7 +2088,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, in += rv; - DEBUGF("inflatehd: %zd bytes read\n", rv); + DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); @@ -2081,8 +2128,8 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_READ_VALUEHUFF; - rv = nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1, - mem); + rv = + nghttp2_rcbuf_new(&inflater->valuercbuf, inflater->left * 2 + 1, mem); } else { inflater->state = NGHTTP2_HD_STATE_READ_VALUE; @@ -2107,7 +2154,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, in += rv; - DEBUGF("inflatehd: %zd bytes read\n", rv); + DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); @@ -2131,18 +2178,18 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_OPCODE; *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); case NGHTTP2_HD_STATE_READ_VALUE: rv = hd_inflate_read(inflater, &inflater->valuebuf, in, last); if (rv < 0) { - DEBUGF("inflatehd: value read failure %zd: %s\n", rv, + DEBUGF("inflatehd: value read failure %td: %s\n", rv, nghttp2_strerror((int)rv)); goto fail; } in += rv; - DEBUGF("inflatehd: %zd bytes read\n", rv); + DEBUGF("inflatehd: %td bytes read\n", rv); if (inflater->left) { DEBUGF("inflatehd: still %zu bytes to go\n", inflater->left); @@ -2165,7 +2212,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, inflater->state = NGHTTP2_HD_STATE_OPCODE; *inflate_flags |= NGHTTP2_HD_INFLATE_EMIT; - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); } } @@ -2185,7 +2232,7 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, } *inflate_flags |= NGHTTP2_HD_INFLATE_FINAL; } - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); almost_ok: if (in_final) { @@ -2195,10 +2242,10 @@ ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, goto fail; } - return (ssize_t)(in - first); + return (nghttp2_ssize)(in - first); fail: - DEBUGF("inflatehd: error return %zd\n", rv); + DEBUGF("inflatehd: error return %td\n", rv); inflater->ctx.bad = 1; return rv; @@ -2253,7 +2300,6 @@ void nghttp2_hd_inflate_del(nghttp2_hd_inflater *inflater) { int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t idx, nghttp2_nv *nv, int indexing_mode) { - return emit_indname_block(bufs, idx, nv, indexing_mode); } @@ -2266,16 +2312,13 @@ int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size) { return emit_table_size(bufs, table_size); } -ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, - uint32_t initial, size_t shift, uint8_t *in, - uint8_t *last, size_t prefix) { +nghttp2_ssize nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, + int *fin, uint32_t initial, size_t shift, + uint8_t *in, uint8_t *last, + size_t prefix) { return decode_length(res, shift_ptr, fin, initial, shift, in, last, prefix); } -static size_t hd_get_num_table_entries(nghttp2_hd_context *context) { - return context->hd_table.len + NGHTTP2_STATIC_TABLE_LENGTH; -} - static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context, size_t idx) { if (idx == 0) { @@ -2292,7 +2335,7 @@ static const nghttp2_nv *hd_get_table_entry(nghttp2_hd_context *context, } size_t nghttp2_hd_deflate_get_num_table_entries(nghttp2_hd_deflater *deflater) { - return hd_get_num_table_entries(&deflater->ctx); + return get_max_index(&deflater->ctx); } const nghttp2_nv * @@ -2311,7 +2354,7 @@ nghttp2_hd_deflate_get_max_dynamic_table_size(nghttp2_hd_deflater *deflater) { } size_t nghttp2_hd_inflate_get_num_table_entries(nghttp2_hd_inflater *inflater) { - return hd_get_num_table_entries(&inflater->ctx); + return get_max_index(&inflater->ctx); } const nghttp2_nv * diff --git a/thirdparty/nghttp2/nghttp2_hd.h b/thirdparty/nghttp2/nghttp2_hd.h index ac104462426..93bd48cff09 100644 --- a/thirdparty/nghttp2/nghttp2_hd.h +++ b/thirdparty/nghttp2/nghttp2_hd.h @@ -106,6 +106,8 @@ typedef enum { NGHTTP2_TOKEN_KEEP_ALIVE, NGHTTP2_TOKEN_PROXY_CONNECTION, NGHTTP2_TOKEN_UPGRADE, + NGHTTP2_TOKEN__PROTOCOL, + NGHTTP2_TOKEN_PRIORITY, } nghttp2_token; struct nghttp2_hd_entry; @@ -206,7 +208,9 @@ typedef struct { #define HD_MAP_SIZE 128 -typedef struct { nghttp2_hd_entry *table[HD_MAP_SIZE]; } nghttp2_hd_map; +typedef struct { + nghttp2_hd_entry *table[HD_MAP_SIZE]; +} nghttp2_hd_map; struct nghttp2_hd_deflater { nghttp2_hd_context ctx; @@ -308,7 +312,7 @@ void nghttp2_hd_deflate_free(nghttp2_hd_deflater *deflater); * * This function expands |bufs| as necessary to store the result. If * buffers is full and the process still requires more space, this - * funtion fails and returns NGHTTP2_ERR_HEADER_COMP. + * function fails and returns NGHTTP2_ERR_HEADER_COMP. * * After this function returns, it is safe to delete the |nva|. * @@ -348,9 +352,10 @@ void nghttp2_hd_inflate_free(nghttp2_hd_inflater *inflater); * that return values and semantics are the same as * nghttp2_hd_inflate_hd(). */ -ssize_t nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, - nghttp2_hd_nv *nv_out, int *inflate_flags, - const uint8_t *in, size_t inlen, int in_final); +nghttp2_ssize nghttp2_hd_inflate_hd_nv(nghttp2_hd_inflater *inflater, + nghttp2_hd_nv *nv_out, + int *inflate_flags, const uint8_t *in, + size_t inlen, int in_final); /* For unittesting purpose */ int nghttp2_hd_emit_indname_block(nghttp2_bufs *bufs, size_t index, @@ -367,9 +372,10 @@ int nghttp2_hd_emit_table_size(nghttp2_bufs *bufs, size_t table_size); nghttp2_hd_nv nghttp2_hd_table_get(nghttp2_hd_context *context, size_t index); /* For unittesting purpose */ -ssize_t nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, int *fin, - uint32_t initial, size_t shift, uint8_t *in, - uint8_t *last, size_t prefix); +nghttp2_ssize nghttp2_hd_decode_length(uint32_t *res, size_t *shift_ptr, + int *fin, uint32_t initial, size_t shift, + uint8_t *in, uint8_t *last, + size_t prefix); /* Huffman encoding/decoding functions */ @@ -418,8 +424,14 @@ void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx); * NGHTTP2_ERR_HEADER_COMP * Decoding process has failed. */ -ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, - nghttp2_buf *buf, const uint8_t *src, - size_t srclen, int fin); +nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_buf *buf, const uint8_t *src, + size_t srclen, int fin); + +/* + * nghttp2_hd_huff_decode_failure_state returns nonzero if |ctx| + * indicates that huffman decoding context is in failure state. + */ +int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx); #endif /* NGHTTP2_HD_H */ diff --git a/thirdparty/nghttp2/nghttp2_hd_huffman.c b/thirdparty/nghttp2/nghttp2_hd_huffman.c index 8881aacb2e6..06db7f5df3f 100644 --- a/thirdparty/nghttp2/nghttp2_hd_huffman.c +++ b/thirdparty/nghttp2/nghttp2_hd_huffman.c @@ -30,114 +30,6 @@ #include "nghttp2_hd.h" -/* - * Encodes huffman code |sym| into |*dest_ptr|, whose least |rembits| - * bits are not filled yet. The |rembits| must be in range [1, 8], - * inclusive. At the end of the process, the |*dest_ptr| is updated - * and points where next output should be placed. The number of - * unfilled bits in the pointed location is returned. - */ -static ssize_t huff_encode_sym(nghttp2_bufs *bufs, size_t *avail_ptr, - size_t rembits, const nghttp2_huff_sym *sym) { - int rv; - size_t nbits = sym->nbits; - uint32_t code = sym->code; - - /* We assume that sym->nbits <= 32 */ - if (rembits > nbits) { - nghttp2_bufs_fast_orb_hold(bufs, (uint8_t)(code << (rembits - nbits))); - return (ssize_t)(rembits - nbits); - } - - if (rembits == nbits) { - nghttp2_bufs_fast_orb(bufs, (uint8_t)code); - --*avail_ptr; - return 8; - } - - nghttp2_bufs_fast_orb(bufs, (uint8_t)(code >> (nbits - rembits))); - --*avail_ptr; - - nbits -= rembits; - if (nbits & 0x7) { - /* align code to MSB byte boundary */ - code <<= 8 - (nbits & 0x7); - } - - if (*avail_ptr < (nbits + 7) / 8) { - /* slow path */ - if (nbits > 24) { - rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 24)); - if (rv != 0) { - return rv; - } - nbits -= 8; - } - if (nbits > 16) { - rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 16)); - if (rv != 0) { - return rv; - } - nbits -= 8; - } - if (nbits > 8) { - rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 8)); - if (rv != 0) { - return rv; - } - nbits -= 8; - } - if (nbits == 8) { - rv = nghttp2_bufs_addb(bufs, (uint8_t)code); - if (rv != 0) { - return rv; - } - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return 8; - } - - rv = nghttp2_bufs_addb_hold(bufs, (uint8_t)code); - if (rv != 0) { - return rv; - } - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return (ssize_t)(8 - nbits); - } - - /* fast path, since most code is less than 8 */ - if (nbits < 8) { - nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code); - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return (ssize_t)(8 - nbits); - } - - /* handle longer code path */ - if (nbits > 24) { - nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 24)); - nbits -= 8; - } - - if (nbits > 16) { - nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 16)); - nbits -= 8; - } - - if (nbits > 8) { - nghttp2_bufs_fast_addb(bufs, (uint8_t)(code >> 8)); - nbits -= 8; - } - - if (nbits == 8) { - nghttp2_bufs_fast_addb(bufs, (uint8_t)code); - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return 8; - } - - nghttp2_bufs_fast_addb_hold(bufs, (uint8_t)code); - *avail_ptr = nghttp2_bufs_cur_avail(bufs); - return (ssize_t)(8 - nbits); -} - size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { size_t i; size_t nbits = 0; @@ -151,81 +43,103 @@ size_t nghttp2_hd_huff_encode_count(const uint8_t *src, size_t len) { int nghttp2_hd_huff_encode(nghttp2_bufs *bufs, const uint8_t *src, size_t srclen) { - int rv; - ssize_t rembits = 8; - size_t i; + const nghttp2_huff_sym *sym; + const uint8_t *end = src + srclen; + uint64_t code = 0; + uint32_t x; + size_t nbits = 0; size_t avail; + int rv; avail = nghttp2_bufs_cur_avail(bufs); - for (i = 0; i < srclen; ++i) { - const nghttp2_huff_sym *sym = &huff_sym_table[src[i]]; - if (rembits == 8) { - if (avail) { - nghttp2_bufs_fast_addb_hold(bufs, 0); - } else { - rv = nghttp2_bufs_addb_hold(bufs, 0); - if (rv != 0) { - return rv; - } - avail = nghttp2_bufs_cur_avail(bufs); + for (; src != end;) { + sym = &huff_sym_table[*src++]; + code |= (uint64_t)sym->code << (32 - nbits); + nbits += sym->nbits; + if (nbits < 32) { + continue; + } + if (avail >= 4) { + x = htonl((uint32_t)(code >> 32)); + memcpy(bufs->cur->buf.last, &x, 4); + bufs->cur->buf.last += 4; + avail -= 4; + code <<= 32; + nbits -= 32; + continue; + } + + for (; nbits >= 8;) { + rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); + if (rv != 0) { + return rv; } + code <<= 8; + nbits -= 8; } - rembits = huff_encode_sym(bufs, &avail, (size_t)rembits, sym); - if (rembits < 0) { - return (int)rembits; + + avail = nghttp2_bufs_cur_avail(bufs); + } + + for (; nbits >= 8;) { + rv = nghttp2_bufs_addb(bufs, (uint8_t)(code >> 56)); + if (rv != 0) { + return rv; } + code <<= 8; + nbits -= 8; } - /* 256 is special terminal symbol, pad with its prefix */ - if (rembits < 8) { - /* if rembits < 8, we should have at least 1 buffer space - available */ - const nghttp2_huff_sym *sym = &huff_sym_table[256]; - assert(avail); - /* Caution we no longer adjust avail here */ - nghttp2_bufs_fast_orb( - bufs, (uint8_t)(sym->code >> (sym->nbits - (size_t)rembits))); + + if (nbits) { + rv = nghttp2_bufs_addb( + bufs, (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1))); + if (rv != 0) { + return rv; + } } return 0; } void nghttp2_hd_huff_decode_context_init(nghttp2_hd_huff_decode_context *ctx) { - ctx->state = 0; - ctx->accept = 1; + ctx->fstate = NGHTTP2_HUFF_ACCEPTED; } -ssize_t nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, - nghttp2_buf *buf, const uint8_t *src, - size_t srclen, int final) { - size_t i; +nghttp2_ssize nghttp2_hd_huff_decode(nghttp2_hd_huff_decode_context *ctx, + nghttp2_buf *buf, const uint8_t *src, + size_t srclen, int final) { + const uint8_t *end = src + srclen; + nghttp2_huff_decode node = {ctx->fstate, 0}; + const nghttp2_huff_decode *t = &node; + uint8_t c; /* We use the decoding algorithm described in - http://graphics.ics.uci.edu/pub/Prefix.pdf */ - for (i = 0; i < srclen; ++i) { - const nghttp2_huff_decode *t; - - t = &huff_decode_table[ctx->state][src[i] >> 4]; - if (t->flags & NGHTTP2_HUFF_FAIL) { - return NGHTTP2_ERR_HEADER_COMP; - } - if (t->flags & NGHTTP2_HUFF_SYM) { + - http://graphics.ics.uci.edu/pub/Prefix.pdf [!!! NO LONGER VALID !!!] + - https://ics.uci.edu/~dan/pubs/Prefix.pdf + - https://github.com/nghttp2/nghttp2/files/15141264/Prefix.pdf */ + for (; src != end;) { + c = *src++; + t = &huff_decode_table[t->fstate & 0x1ff][c >> 4]; + if (t->fstate & NGHTTP2_HUFF_SYM) { *buf->last++ = t->sym; } - t = &huff_decode_table[t->state][src[i] & 0xf]; - if (t->flags & NGHTTP2_HUFF_FAIL) { - return NGHTTP2_ERR_HEADER_COMP; - } - if (t->flags & NGHTTP2_HUFF_SYM) { + t = &huff_decode_table[t->fstate & 0x1ff][c & 0xf]; + if (t->fstate & NGHTTP2_HUFF_SYM) { *buf->last++ = t->sym; } - - ctx->state = t->state; - ctx->accept = (t->flags & NGHTTP2_HUFF_ACCEPTED) != 0; } - if (final && !ctx->accept) { + + ctx->fstate = t->fstate; + + if (final && !(ctx->fstate & NGHTTP2_HUFF_ACCEPTED)) { return NGHTTP2_ERR_HEADER_COMP; } - return (ssize_t)i; + + return (nghttp2_ssize)srclen; +} + +int nghttp2_hd_huff_decode_failure_state(nghttp2_hd_huff_decode_context *ctx) { + return ctx->fstate == 0x100; } diff --git a/thirdparty/nghttp2/nghttp2_hd_huffman.h b/thirdparty/nghttp2/nghttp2_hd_huffman.h index eadb7566a73..4bd850d9975 100644 --- a/thirdparty/nghttp2/nghttp2_hd_huffman.h +++ b/thirdparty/nghttp2/nghttp2_hd_huffman.h @@ -26,25 +26,25 @@ #define NGHTTP2_HD_HUFFMAN_H #include +#include typedef enum { /* FSA accepts this state as the end of huffman encoding sequence. */ - NGHTTP2_HUFF_ACCEPTED = 1, + NGHTTP2_HUFF_ACCEPTED = 1 << 14, /* This state emits symbol */ - NGHTTP2_HUFF_SYM = (1 << 1), - /* If state machine reaches this state, decoding fails. */ - NGHTTP2_HUFF_FAIL = (1 << 2) + NGHTTP2_HUFF_SYM = 1 << 15, } nghttp2_huff_decode_flag; typedef struct { - /* huffman decoding state, which is actually the node ID of internal - huffman tree. We have 257 leaf nodes, but they are identical to - root node other than emitting a symbol, so we have 256 internal - nodes [1..255], inclusive. */ - uint8_t state; - /* bitwise OR of zero or more of the nghttp2_huff_decode_flag */ - uint8_t flags; + /* fstate is the current huffman decoding state, which is actually + the node ID of internal huffman tree with + nghttp2_huff_decode_flag OR-ed. We have 257 leaf nodes, but they + are identical to root node other than emitting a symbol, so we + have 256 internal nodes [1..255], inclusive. The node ID 256 is + a special node and it is a terminal state that means decoding + failed. */ + uint16_t fstate; /* symbol if NGHTTP2_HUFF_SYM flag set */ uint8_t sym; } nghttp2_huff_decode; @@ -52,12 +52,8 @@ typedef struct { typedef nghttp2_huff_decode huff_decode_table_type[16]; typedef struct { - /* Current huffman decoding state. We stripped leaf nodes, so the - value range is [0..255], inclusive. */ - uint8_t state; - /* nonzero if we can say that the decoding process succeeds at this - state */ - uint8_t accept; + /* fstate is the current huffman decoding state. */ + uint16_t fstate; } nghttp2_hd_huff_decode_context; typedef struct { diff --git a/thirdparty/nghttp2/nghttp2_hd_huffman_data.c b/thirdparty/nghttp2/nghttp2_hd_huffman_data.c index 5ef4a956b93..c8f4a6fa266 100644 --- a/thirdparty/nghttp2/nghttp2_hd_huffman_data.c +++ b/thirdparty/nghttp2/nghttp2_hd_huffman_data.c @@ -27,4935 +27,4954 @@ /* Generated by mkhufftbl.py */ const nghttp2_huff_sym huff_sym_table[] = { - {13, 0x1ff8u}, {23, 0x7fffd8u}, {28, 0xfffffe2u}, {28, 0xfffffe3u}, - {28, 0xfffffe4u}, {28, 0xfffffe5u}, {28, 0xfffffe6u}, {28, 0xfffffe7u}, - {28, 0xfffffe8u}, {24, 0xffffeau}, {30, 0x3ffffffcu}, {28, 0xfffffe9u}, - {28, 0xfffffeau}, {30, 0x3ffffffdu}, {28, 0xfffffebu}, {28, 0xfffffecu}, - {28, 0xfffffedu}, {28, 0xfffffeeu}, {28, 0xfffffefu}, {28, 0xffffff0u}, - {28, 0xffffff1u}, {28, 0xffffff2u}, {30, 0x3ffffffeu}, {28, 0xffffff3u}, - {28, 0xffffff4u}, {28, 0xffffff5u}, {28, 0xffffff6u}, {28, 0xffffff7u}, - {28, 0xffffff8u}, {28, 0xffffff9u}, {28, 0xffffffau}, {28, 0xffffffbu}, - {6, 0x14u}, {10, 0x3f8u}, {10, 0x3f9u}, {12, 0xffau}, - {13, 0x1ff9u}, {6, 0x15u}, {8, 0xf8u}, {11, 0x7fau}, - {10, 0x3fau}, {10, 0x3fbu}, {8, 0xf9u}, {11, 0x7fbu}, - {8, 0xfau}, {6, 0x16u}, {6, 0x17u}, {6, 0x18u}, - {5, 0x0u}, {5, 0x1u}, {5, 0x2u}, {6, 0x19u}, - {6, 0x1au}, {6, 0x1bu}, {6, 0x1cu}, {6, 0x1du}, - {6, 0x1eu}, {6, 0x1fu}, {7, 0x5cu}, {8, 0xfbu}, - {15, 0x7ffcu}, {6, 0x20u}, {12, 0xffbu}, {10, 0x3fcu}, - {13, 0x1ffau}, {6, 0x21u}, {7, 0x5du}, {7, 0x5eu}, - {7, 0x5fu}, {7, 0x60u}, {7, 0x61u}, {7, 0x62u}, - {7, 0x63u}, {7, 0x64u}, {7, 0x65u}, {7, 0x66u}, - {7, 0x67u}, {7, 0x68u}, {7, 0x69u}, {7, 0x6au}, - {7, 0x6bu}, {7, 0x6cu}, {7, 0x6du}, {7, 0x6eu}, - {7, 0x6fu}, {7, 0x70u}, {7, 0x71u}, {7, 0x72u}, - {8, 0xfcu}, {7, 0x73u}, {8, 0xfdu}, {13, 0x1ffbu}, - {19, 0x7fff0u}, {13, 0x1ffcu}, {14, 0x3ffcu}, {6, 0x22u}, - {15, 0x7ffdu}, {5, 0x3u}, {6, 0x23u}, {5, 0x4u}, - {6, 0x24u}, {5, 0x5u}, {6, 0x25u}, {6, 0x26u}, - {6, 0x27u}, {5, 0x6u}, {7, 0x74u}, {7, 0x75u}, - {6, 0x28u}, {6, 0x29u}, {6, 0x2au}, {5, 0x7u}, - {6, 0x2bu}, {7, 0x76u}, {6, 0x2cu}, {5, 0x8u}, - {5, 0x9u}, {6, 0x2du}, {7, 0x77u}, {7, 0x78u}, - {7, 0x79u}, {7, 0x7au}, {7, 0x7bu}, {15, 0x7ffeu}, - {11, 0x7fcu}, {14, 0x3ffdu}, {13, 0x1ffdu}, {28, 0xffffffcu}, - {20, 0xfffe6u}, {22, 0x3fffd2u}, {20, 0xfffe7u}, {20, 0xfffe8u}, - {22, 0x3fffd3u}, {22, 0x3fffd4u}, {22, 0x3fffd5u}, {23, 0x7fffd9u}, - {22, 0x3fffd6u}, {23, 0x7fffdau}, {23, 0x7fffdbu}, {23, 0x7fffdcu}, - {23, 0x7fffddu}, {23, 0x7fffdeu}, {24, 0xffffebu}, {23, 0x7fffdfu}, - {24, 0xffffecu}, {24, 0xffffedu}, {22, 0x3fffd7u}, {23, 0x7fffe0u}, - {24, 0xffffeeu}, {23, 0x7fffe1u}, {23, 0x7fffe2u}, {23, 0x7fffe3u}, - {23, 0x7fffe4u}, {21, 0x1fffdcu}, {22, 0x3fffd8u}, {23, 0x7fffe5u}, - {22, 0x3fffd9u}, {23, 0x7fffe6u}, {23, 0x7fffe7u}, {24, 0xffffefu}, - {22, 0x3fffdau}, {21, 0x1fffddu}, {20, 0xfffe9u}, {22, 0x3fffdbu}, - {22, 0x3fffdcu}, {23, 0x7fffe8u}, {23, 0x7fffe9u}, {21, 0x1fffdeu}, - {23, 0x7fffeau}, {22, 0x3fffddu}, {22, 0x3fffdeu}, {24, 0xfffff0u}, - {21, 0x1fffdfu}, {22, 0x3fffdfu}, {23, 0x7fffebu}, {23, 0x7fffecu}, - {21, 0x1fffe0u}, {21, 0x1fffe1u}, {22, 0x3fffe0u}, {21, 0x1fffe2u}, - {23, 0x7fffedu}, {22, 0x3fffe1u}, {23, 0x7fffeeu}, {23, 0x7fffefu}, - {20, 0xfffeau}, {22, 0x3fffe2u}, {22, 0x3fffe3u}, {22, 0x3fffe4u}, - {23, 0x7ffff0u}, {22, 0x3fffe5u}, {22, 0x3fffe6u}, {23, 0x7ffff1u}, - {26, 0x3ffffe0u}, {26, 0x3ffffe1u}, {20, 0xfffebu}, {19, 0x7fff1u}, - {22, 0x3fffe7u}, {23, 0x7ffff2u}, {22, 0x3fffe8u}, {25, 0x1ffffecu}, - {26, 0x3ffffe2u}, {26, 0x3ffffe3u}, {26, 0x3ffffe4u}, {27, 0x7ffffdeu}, - {27, 0x7ffffdfu}, {26, 0x3ffffe5u}, {24, 0xfffff1u}, {25, 0x1ffffedu}, - {19, 0x7fff2u}, {21, 0x1fffe3u}, {26, 0x3ffffe6u}, {27, 0x7ffffe0u}, - {27, 0x7ffffe1u}, {26, 0x3ffffe7u}, {27, 0x7ffffe2u}, {24, 0xfffff2u}, - {21, 0x1fffe4u}, {21, 0x1fffe5u}, {26, 0x3ffffe8u}, {26, 0x3ffffe9u}, - {28, 0xffffffdu}, {27, 0x7ffffe3u}, {27, 0x7ffffe4u}, {27, 0x7ffffe5u}, - {20, 0xfffecu}, {24, 0xfffff3u}, {20, 0xfffedu}, {21, 0x1fffe6u}, - {22, 0x3fffe9u}, {21, 0x1fffe7u}, {21, 0x1fffe8u}, {23, 0x7ffff3u}, - {22, 0x3fffeau}, {22, 0x3fffebu}, {25, 0x1ffffeeu}, {25, 0x1ffffefu}, - {24, 0xfffff4u}, {24, 0xfffff5u}, {26, 0x3ffffeau}, {23, 0x7ffff4u}, - {26, 0x3ffffebu}, {27, 0x7ffffe6u}, {26, 0x3ffffecu}, {26, 0x3ffffedu}, - {27, 0x7ffffe7u}, {27, 0x7ffffe8u}, {27, 0x7ffffe9u}, {27, 0x7ffffeau}, - {27, 0x7ffffebu}, {28, 0xffffffeu}, {27, 0x7ffffecu}, {27, 0x7ffffedu}, - {27, 0x7ffffeeu}, {27, 0x7ffffefu}, {27, 0x7fffff0u}, {26, 0x3ffffeeu}, - {30, 0x3fffffffu}}; + {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, + {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, + {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, + {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u}, + {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u}, + {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u}, + {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u}, + {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u}, + {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u}, + {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u}, + {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u}, + {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u}, + {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u}, + {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u}, + {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u}, + {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u}, + {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u}, + {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u}, + {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u}, + {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u}, + {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u}, + {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u}, + {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u}, + {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u}, + {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u}, + {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u}, + {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u}, + {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u}, + {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u}, + {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u}, + {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u}, + {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u}, + {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u}, + {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u}, + {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u}, + {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u}, + {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u}, + {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u}, + {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u}, + {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u}, + {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u}, + {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u}, + {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u}, + {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u}, + {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u}, + {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u}, + {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u}, + {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u}, + {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u}, + {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u}, + {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u}, + {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u}, + {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u}, + {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u}, + {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u}, + {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u}, + {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u}, + {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u}, + {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u}, + {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u}, + {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u}, + {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u}, + {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, + {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, + {30, 0xfffffffcu}}; const nghttp2_huff_decode huff_decode_table[][16] = { - /* 0 */ - { - {4, 0x00, 0}, - {5, 0x00, 0}, - {7, 0x00, 0}, - {8, 0x00, 0}, - {11, 0x00, 0}, - {12, 0x00, 0}, - {16, 0x00, 0}, - {19, 0x00, 0}, - {25, 0x00, 0}, - {28, 0x00, 0}, - {32, 0x00, 0}, - {35, 0x00, 0}, - {42, 0x00, 0}, - {49, 0x00, 0}, - {57, 0x00, 0}, - {64, 0x01, 0}, - }, - /* 1 */ - { - {0, 0x03, 48}, - {0, 0x03, 49}, - {0, 0x03, 50}, - {0, 0x03, 97}, - {0, 0x03, 99}, - {0, 0x03, 101}, - {0, 0x03, 105}, - {0, 0x03, 111}, - {0, 0x03, 115}, - {0, 0x03, 116}, - {13, 0x00, 0}, - {14, 0x00, 0}, - {17, 0x00, 0}, - {18, 0x00, 0}, - {20, 0x00, 0}, - {21, 0x00, 0}, - }, - /* 2 */ - { - {1, 0x02, 48}, - {22, 0x03, 48}, - {1, 0x02, 49}, - {22, 0x03, 49}, - {1, 0x02, 50}, - {22, 0x03, 50}, - {1, 0x02, 97}, - {22, 0x03, 97}, - {1, 0x02, 99}, - {22, 0x03, 99}, - {1, 0x02, 101}, - {22, 0x03, 101}, - {1, 0x02, 105}, - {22, 0x03, 105}, - {1, 0x02, 111}, - {22, 0x03, 111}, - }, - /* 3 */ - { - {2, 0x02, 48}, - {9, 0x02, 48}, - {23, 0x02, 48}, - {40, 0x03, 48}, - {2, 0x02, 49}, - {9, 0x02, 49}, - {23, 0x02, 49}, - {40, 0x03, 49}, - {2, 0x02, 50}, - {9, 0x02, 50}, - {23, 0x02, 50}, - {40, 0x03, 50}, - {2, 0x02, 97}, - {9, 0x02, 97}, - {23, 0x02, 97}, - {40, 0x03, 97}, - }, - /* 4 */ - { - {3, 0x02, 48}, - {6, 0x02, 48}, - {10, 0x02, 48}, - {15, 0x02, 48}, - {24, 0x02, 48}, - {31, 0x02, 48}, - {41, 0x02, 48}, - {56, 0x03, 48}, - {3, 0x02, 49}, - {6, 0x02, 49}, - {10, 0x02, 49}, - {15, 0x02, 49}, - {24, 0x02, 49}, - {31, 0x02, 49}, - {41, 0x02, 49}, - {56, 0x03, 49}, - }, - /* 5 */ - { - {3, 0x02, 50}, - {6, 0x02, 50}, - {10, 0x02, 50}, - {15, 0x02, 50}, - {24, 0x02, 50}, - {31, 0x02, 50}, - {41, 0x02, 50}, - {56, 0x03, 50}, - {3, 0x02, 97}, - {6, 0x02, 97}, - {10, 0x02, 97}, - {15, 0x02, 97}, - {24, 0x02, 97}, - {31, 0x02, 97}, - {41, 0x02, 97}, - {56, 0x03, 97}, - }, - /* 6 */ - { - {2, 0x02, 99}, - {9, 0x02, 99}, - {23, 0x02, 99}, - {40, 0x03, 99}, - {2, 0x02, 101}, - {9, 0x02, 101}, - {23, 0x02, 101}, - {40, 0x03, 101}, - {2, 0x02, 105}, - {9, 0x02, 105}, - {23, 0x02, 105}, - {40, 0x03, 105}, - {2, 0x02, 111}, - {9, 0x02, 111}, - {23, 0x02, 111}, - {40, 0x03, 111}, - }, - /* 7 */ - { - {3, 0x02, 99}, - {6, 0x02, 99}, - {10, 0x02, 99}, - {15, 0x02, 99}, - {24, 0x02, 99}, - {31, 0x02, 99}, - {41, 0x02, 99}, - {56, 0x03, 99}, - {3, 0x02, 101}, - {6, 0x02, 101}, - {10, 0x02, 101}, - {15, 0x02, 101}, - {24, 0x02, 101}, - {31, 0x02, 101}, - {41, 0x02, 101}, - {56, 0x03, 101}, - }, - /* 8 */ - { - {3, 0x02, 105}, - {6, 0x02, 105}, - {10, 0x02, 105}, - {15, 0x02, 105}, - {24, 0x02, 105}, - {31, 0x02, 105}, - {41, 0x02, 105}, - {56, 0x03, 105}, - {3, 0x02, 111}, - {6, 0x02, 111}, - {10, 0x02, 111}, - {15, 0x02, 111}, - {24, 0x02, 111}, - {31, 0x02, 111}, - {41, 0x02, 111}, - {56, 0x03, 111}, - }, - /* 9 */ - { - {1, 0x02, 115}, - {22, 0x03, 115}, - {1, 0x02, 116}, - {22, 0x03, 116}, - {0, 0x03, 32}, - {0, 0x03, 37}, - {0, 0x03, 45}, - {0, 0x03, 46}, - {0, 0x03, 47}, - {0, 0x03, 51}, - {0, 0x03, 52}, - {0, 0x03, 53}, - {0, 0x03, 54}, - {0, 0x03, 55}, - {0, 0x03, 56}, - {0, 0x03, 57}, - }, - /* 10 */ - { - {2, 0x02, 115}, - {9, 0x02, 115}, - {23, 0x02, 115}, - {40, 0x03, 115}, - {2, 0x02, 116}, - {9, 0x02, 116}, - {23, 0x02, 116}, - {40, 0x03, 116}, - {1, 0x02, 32}, - {22, 0x03, 32}, - {1, 0x02, 37}, - {22, 0x03, 37}, - {1, 0x02, 45}, - {22, 0x03, 45}, - {1, 0x02, 46}, - {22, 0x03, 46}, - }, - /* 11 */ - { - {3, 0x02, 115}, - {6, 0x02, 115}, - {10, 0x02, 115}, - {15, 0x02, 115}, - {24, 0x02, 115}, - {31, 0x02, 115}, - {41, 0x02, 115}, - {56, 0x03, 115}, - {3, 0x02, 116}, - {6, 0x02, 116}, - {10, 0x02, 116}, - {15, 0x02, 116}, - {24, 0x02, 116}, - {31, 0x02, 116}, - {41, 0x02, 116}, - {56, 0x03, 116}, - }, - /* 12 */ - { - {2, 0x02, 32}, - {9, 0x02, 32}, - {23, 0x02, 32}, - {40, 0x03, 32}, - {2, 0x02, 37}, - {9, 0x02, 37}, - {23, 0x02, 37}, - {40, 0x03, 37}, - {2, 0x02, 45}, - {9, 0x02, 45}, - {23, 0x02, 45}, - {40, 0x03, 45}, - {2, 0x02, 46}, - {9, 0x02, 46}, - {23, 0x02, 46}, - {40, 0x03, 46}, - }, - /* 13 */ - { - {3, 0x02, 32}, - {6, 0x02, 32}, - {10, 0x02, 32}, - {15, 0x02, 32}, - {24, 0x02, 32}, - {31, 0x02, 32}, - {41, 0x02, 32}, - {56, 0x03, 32}, - {3, 0x02, 37}, - {6, 0x02, 37}, - {10, 0x02, 37}, - {15, 0x02, 37}, - {24, 0x02, 37}, - {31, 0x02, 37}, - {41, 0x02, 37}, - {56, 0x03, 37}, - }, - /* 14 */ - { - {3, 0x02, 45}, - {6, 0x02, 45}, - {10, 0x02, 45}, - {15, 0x02, 45}, - {24, 0x02, 45}, - {31, 0x02, 45}, - {41, 0x02, 45}, - {56, 0x03, 45}, - {3, 0x02, 46}, - {6, 0x02, 46}, - {10, 0x02, 46}, - {15, 0x02, 46}, - {24, 0x02, 46}, - {31, 0x02, 46}, - {41, 0x02, 46}, - {56, 0x03, 46}, - }, - /* 15 */ - { - {1, 0x02, 47}, - {22, 0x03, 47}, - {1, 0x02, 51}, - {22, 0x03, 51}, - {1, 0x02, 52}, - {22, 0x03, 52}, - {1, 0x02, 53}, - {22, 0x03, 53}, - {1, 0x02, 54}, - {22, 0x03, 54}, - {1, 0x02, 55}, - {22, 0x03, 55}, - {1, 0x02, 56}, - {22, 0x03, 56}, - {1, 0x02, 57}, - {22, 0x03, 57}, - }, - /* 16 */ - { - {2, 0x02, 47}, - {9, 0x02, 47}, - {23, 0x02, 47}, - {40, 0x03, 47}, - {2, 0x02, 51}, - {9, 0x02, 51}, - {23, 0x02, 51}, - {40, 0x03, 51}, - {2, 0x02, 52}, - {9, 0x02, 52}, - {23, 0x02, 52}, - {40, 0x03, 52}, - {2, 0x02, 53}, - {9, 0x02, 53}, - {23, 0x02, 53}, - {40, 0x03, 53}, - }, - /* 17 */ - { - {3, 0x02, 47}, - {6, 0x02, 47}, - {10, 0x02, 47}, - {15, 0x02, 47}, - {24, 0x02, 47}, - {31, 0x02, 47}, - {41, 0x02, 47}, - {56, 0x03, 47}, - {3, 0x02, 51}, - {6, 0x02, 51}, - {10, 0x02, 51}, - {15, 0x02, 51}, - {24, 0x02, 51}, - {31, 0x02, 51}, - {41, 0x02, 51}, - {56, 0x03, 51}, - }, - /* 18 */ - { - {3, 0x02, 52}, - {6, 0x02, 52}, - {10, 0x02, 52}, - {15, 0x02, 52}, - {24, 0x02, 52}, - {31, 0x02, 52}, - {41, 0x02, 52}, - {56, 0x03, 52}, - {3, 0x02, 53}, - {6, 0x02, 53}, - {10, 0x02, 53}, - {15, 0x02, 53}, - {24, 0x02, 53}, - {31, 0x02, 53}, - {41, 0x02, 53}, - {56, 0x03, 53}, - }, - /* 19 */ - { - {2, 0x02, 54}, - {9, 0x02, 54}, - {23, 0x02, 54}, - {40, 0x03, 54}, - {2, 0x02, 55}, - {9, 0x02, 55}, - {23, 0x02, 55}, - {40, 0x03, 55}, - {2, 0x02, 56}, - {9, 0x02, 56}, - {23, 0x02, 56}, - {40, 0x03, 56}, - {2, 0x02, 57}, - {9, 0x02, 57}, - {23, 0x02, 57}, - {40, 0x03, 57}, - }, - /* 20 */ - { - {3, 0x02, 54}, - {6, 0x02, 54}, - {10, 0x02, 54}, - {15, 0x02, 54}, - {24, 0x02, 54}, - {31, 0x02, 54}, - {41, 0x02, 54}, - {56, 0x03, 54}, - {3, 0x02, 55}, - {6, 0x02, 55}, - {10, 0x02, 55}, - {15, 0x02, 55}, - {24, 0x02, 55}, - {31, 0x02, 55}, - {41, 0x02, 55}, - {56, 0x03, 55}, - }, - /* 21 */ - { - {3, 0x02, 56}, - {6, 0x02, 56}, - {10, 0x02, 56}, - {15, 0x02, 56}, - {24, 0x02, 56}, - {31, 0x02, 56}, - {41, 0x02, 56}, - {56, 0x03, 56}, - {3, 0x02, 57}, - {6, 0x02, 57}, - {10, 0x02, 57}, - {15, 0x02, 57}, - {24, 0x02, 57}, - {31, 0x02, 57}, - {41, 0x02, 57}, - {56, 0x03, 57}, - }, - /* 22 */ - { - {26, 0x00, 0}, - {27, 0x00, 0}, - {29, 0x00, 0}, - {30, 0x00, 0}, - {33, 0x00, 0}, - {34, 0x00, 0}, - {36, 0x00, 0}, - {37, 0x00, 0}, - {43, 0x00, 0}, - {46, 0x00, 0}, - {50, 0x00, 0}, - {53, 0x00, 0}, - {58, 0x00, 0}, - {61, 0x00, 0}, - {65, 0x00, 0}, - {68, 0x01, 0}, - }, - /* 23 */ - { - {0, 0x03, 61}, - {0, 0x03, 65}, - {0, 0x03, 95}, - {0, 0x03, 98}, - {0, 0x03, 100}, - {0, 0x03, 102}, - {0, 0x03, 103}, - {0, 0x03, 104}, - {0, 0x03, 108}, - {0, 0x03, 109}, - {0, 0x03, 110}, - {0, 0x03, 112}, - {0, 0x03, 114}, - {0, 0x03, 117}, - {38, 0x00, 0}, - {39, 0x00, 0}, - }, - /* 24 */ - { - {1, 0x02, 61}, - {22, 0x03, 61}, - {1, 0x02, 65}, - {22, 0x03, 65}, - {1, 0x02, 95}, - {22, 0x03, 95}, - {1, 0x02, 98}, - {22, 0x03, 98}, - {1, 0x02, 100}, - {22, 0x03, 100}, - {1, 0x02, 102}, - {22, 0x03, 102}, - {1, 0x02, 103}, - {22, 0x03, 103}, - {1, 0x02, 104}, - {22, 0x03, 104}, - }, - /* 25 */ - { - {2, 0x02, 61}, - {9, 0x02, 61}, - {23, 0x02, 61}, - {40, 0x03, 61}, - {2, 0x02, 65}, - {9, 0x02, 65}, - {23, 0x02, 65}, - {40, 0x03, 65}, - {2, 0x02, 95}, - {9, 0x02, 95}, - {23, 0x02, 95}, - {40, 0x03, 95}, - {2, 0x02, 98}, - {9, 0x02, 98}, - {23, 0x02, 98}, - {40, 0x03, 98}, - }, - /* 26 */ - { - {3, 0x02, 61}, - {6, 0x02, 61}, - {10, 0x02, 61}, - {15, 0x02, 61}, - {24, 0x02, 61}, - {31, 0x02, 61}, - {41, 0x02, 61}, - {56, 0x03, 61}, - {3, 0x02, 65}, - {6, 0x02, 65}, - {10, 0x02, 65}, - {15, 0x02, 65}, - {24, 0x02, 65}, - {31, 0x02, 65}, - {41, 0x02, 65}, - {56, 0x03, 65}, - }, - /* 27 */ - { - {3, 0x02, 95}, - {6, 0x02, 95}, - {10, 0x02, 95}, - {15, 0x02, 95}, - {24, 0x02, 95}, - {31, 0x02, 95}, - {41, 0x02, 95}, - {56, 0x03, 95}, - {3, 0x02, 98}, - {6, 0x02, 98}, - {10, 0x02, 98}, - {15, 0x02, 98}, - {24, 0x02, 98}, - {31, 0x02, 98}, - {41, 0x02, 98}, - {56, 0x03, 98}, - }, - /* 28 */ - { - {2, 0x02, 100}, - {9, 0x02, 100}, - {23, 0x02, 100}, - {40, 0x03, 100}, - {2, 0x02, 102}, - {9, 0x02, 102}, - {23, 0x02, 102}, - {40, 0x03, 102}, - {2, 0x02, 103}, - {9, 0x02, 103}, - {23, 0x02, 103}, - {40, 0x03, 103}, - {2, 0x02, 104}, - {9, 0x02, 104}, - {23, 0x02, 104}, - {40, 0x03, 104}, - }, - /* 29 */ - { - {3, 0x02, 100}, - {6, 0x02, 100}, - {10, 0x02, 100}, - {15, 0x02, 100}, - {24, 0x02, 100}, - {31, 0x02, 100}, - {41, 0x02, 100}, - {56, 0x03, 100}, - {3, 0x02, 102}, - {6, 0x02, 102}, - {10, 0x02, 102}, - {15, 0x02, 102}, - {24, 0x02, 102}, - {31, 0x02, 102}, - {41, 0x02, 102}, - {56, 0x03, 102}, - }, - /* 30 */ - { - {3, 0x02, 103}, - {6, 0x02, 103}, - {10, 0x02, 103}, - {15, 0x02, 103}, - {24, 0x02, 103}, - {31, 0x02, 103}, - {41, 0x02, 103}, - {56, 0x03, 103}, - {3, 0x02, 104}, - {6, 0x02, 104}, - {10, 0x02, 104}, - {15, 0x02, 104}, - {24, 0x02, 104}, - {31, 0x02, 104}, - {41, 0x02, 104}, - {56, 0x03, 104}, - }, - /* 31 */ - { - {1, 0x02, 108}, - {22, 0x03, 108}, - {1, 0x02, 109}, - {22, 0x03, 109}, - {1, 0x02, 110}, - {22, 0x03, 110}, - {1, 0x02, 112}, - {22, 0x03, 112}, - {1, 0x02, 114}, - {22, 0x03, 114}, - {1, 0x02, 117}, - {22, 0x03, 117}, - {0, 0x03, 58}, - {0, 0x03, 66}, - {0, 0x03, 67}, - {0, 0x03, 68}, - }, - /* 32 */ - { - {2, 0x02, 108}, - {9, 0x02, 108}, - {23, 0x02, 108}, - {40, 0x03, 108}, - {2, 0x02, 109}, - {9, 0x02, 109}, - {23, 0x02, 109}, - {40, 0x03, 109}, - {2, 0x02, 110}, - {9, 0x02, 110}, - {23, 0x02, 110}, - {40, 0x03, 110}, - {2, 0x02, 112}, - {9, 0x02, 112}, - {23, 0x02, 112}, - {40, 0x03, 112}, - }, - /* 33 */ - { - {3, 0x02, 108}, - {6, 0x02, 108}, - {10, 0x02, 108}, - {15, 0x02, 108}, - {24, 0x02, 108}, - {31, 0x02, 108}, - {41, 0x02, 108}, - {56, 0x03, 108}, - {3, 0x02, 109}, - {6, 0x02, 109}, - {10, 0x02, 109}, - {15, 0x02, 109}, - {24, 0x02, 109}, - {31, 0x02, 109}, - {41, 0x02, 109}, - {56, 0x03, 109}, - }, - /* 34 */ - { - {3, 0x02, 110}, - {6, 0x02, 110}, - {10, 0x02, 110}, - {15, 0x02, 110}, - {24, 0x02, 110}, - {31, 0x02, 110}, - {41, 0x02, 110}, - {56, 0x03, 110}, - {3, 0x02, 112}, - {6, 0x02, 112}, - {10, 0x02, 112}, - {15, 0x02, 112}, - {24, 0x02, 112}, - {31, 0x02, 112}, - {41, 0x02, 112}, - {56, 0x03, 112}, - }, - /* 35 */ - { - {2, 0x02, 114}, - {9, 0x02, 114}, - {23, 0x02, 114}, - {40, 0x03, 114}, - {2, 0x02, 117}, - {9, 0x02, 117}, - {23, 0x02, 117}, - {40, 0x03, 117}, - {1, 0x02, 58}, - {22, 0x03, 58}, - {1, 0x02, 66}, - {22, 0x03, 66}, - {1, 0x02, 67}, - {22, 0x03, 67}, - {1, 0x02, 68}, - {22, 0x03, 68}, - }, - /* 36 */ - { - {3, 0x02, 114}, - {6, 0x02, 114}, - {10, 0x02, 114}, - {15, 0x02, 114}, - {24, 0x02, 114}, - {31, 0x02, 114}, - {41, 0x02, 114}, - {56, 0x03, 114}, - {3, 0x02, 117}, - {6, 0x02, 117}, - {10, 0x02, 117}, - {15, 0x02, 117}, - {24, 0x02, 117}, - {31, 0x02, 117}, - {41, 0x02, 117}, - {56, 0x03, 117}, - }, - /* 37 */ - { - {2, 0x02, 58}, - {9, 0x02, 58}, - {23, 0x02, 58}, - {40, 0x03, 58}, - {2, 0x02, 66}, - {9, 0x02, 66}, - {23, 0x02, 66}, - {40, 0x03, 66}, - {2, 0x02, 67}, - {9, 0x02, 67}, - {23, 0x02, 67}, - {40, 0x03, 67}, - {2, 0x02, 68}, - {9, 0x02, 68}, - {23, 0x02, 68}, - {40, 0x03, 68}, - }, - /* 38 */ - { - {3, 0x02, 58}, - {6, 0x02, 58}, - {10, 0x02, 58}, - {15, 0x02, 58}, - {24, 0x02, 58}, - {31, 0x02, 58}, - {41, 0x02, 58}, - {56, 0x03, 58}, - {3, 0x02, 66}, - {6, 0x02, 66}, - {10, 0x02, 66}, - {15, 0x02, 66}, - {24, 0x02, 66}, - {31, 0x02, 66}, - {41, 0x02, 66}, - {56, 0x03, 66}, - }, - /* 39 */ - { - {3, 0x02, 67}, - {6, 0x02, 67}, - {10, 0x02, 67}, - {15, 0x02, 67}, - {24, 0x02, 67}, - {31, 0x02, 67}, - {41, 0x02, 67}, - {56, 0x03, 67}, - {3, 0x02, 68}, - {6, 0x02, 68}, - {10, 0x02, 68}, - {15, 0x02, 68}, - {24, 0x02, 68}, - {31, 0x02, 68}, - {41, 0x02, 68}, - {56, 0x03, 68}, - }, - /* 40 */ - { - {44, 0x00, 0}, - {45, 0x00, 0}, - {47, 0x00, 0}, - {48, 0x00, 0}, - {51, 0x00, 0}, - {52, 0x00, 0}, - {54, 0x00, 0}, - {55, 0x00, 0}, - {59, 0x00, 0}, - {60, 0x00, 0}, - {62, 0x00, 0}, - {63, 0x00, 0}, - {66, 0x00, 0}, - {67, 0x00, 0}, - {69, 0x00, 0}, - {72, 0x01, 0}, - }, - /* 41 */ - { - {0, 0x03, 69}, - {0, 0x03, 70}, - {0, 0x03, 71}, - {0, 0x03, 72}, - {0, 0x03, 73}, - {0, 0x03, 74}, - {0, 0x03, 75}, - {0, 0x03, 76}, - {0, 0x03, 77}, - {0, 0x03, 78}, - {0, 0x03, 79}, - {0, 0x03, 80}, - {0, 0x03, 81}, - {0, 0x03, 82}, - {0, 0x03, 83}, - {0, 0x03, 84}, - }, - /* 42 */ - { - {1, 0x02, 69}, - {22, 0x03, 69}, - {1, 0x02, 70}, - {22, 0x03, 70}, - {1, 0x02, 71}, - {22, 0x03, 71}, - {1, 0x02, 72}, - {22, 0x03, 72}, - {1, 0x02, 73}, - {22, 0x03, 73}, - {1, 0x02, 74}, - {22, 0x03, 74}, - {1, 0x02, 75}, - {22, 0x03, 75}, - {1, 0x02, 76}, - {22, 0x03, 76}, - }, - /* 43 */ - { - {2, 0x02, 69}, - {9, 0x02, 69}, - {23, 0x02, 69}, - {40, 0x03, 69}, - {2, 0x02, 70}, - {9, 0x02, 70}, - {23, 0x02, 70}, - {40, 0x03, 70}, - {2, 0x02, 71}, - {9, 0x02, 71}, - {23, 0x02, 71}, - {40, 0x03, 71}, - {2, 0x02, 72}, - {9, 0x02, 72}, - {23, 0x02, 72}, - {40, 0x03, 72}, - }, - /* 44 */ - { - {3, 0x02, 69}, - {6, 0x02, 69}, - {10, 0x02, 69}, - {15, 0x02, 69}, - {24, 0x02, 69}, - {31, 0x02, 69}, - {41, 0x02, 69}, - {56, 0x03, 69}, - {3, 0x02, 70}, - {6, 0x02, 70}, - {10, 0x02, 70}, - {15, 0x02, 70}, - {24, 0x02, 70}, - {31, 0x02, 70}, - {41, 0x02, 70}, - {56, 0x03, 70}, - }, - /* 45 */ - { - {3, 0x02, 71}, - {6, 0x02, 71}, - {10, 0x02, 71}, - {15, 0x02, 71}, - {24, 0x02, 71}, - {31, 0x02, 71}, - {41, 0x02, 71}, - {56, 0x03, 71}, - {3, 0x02, 72}, - {6, 0x02, 72}, - {10, 0x02, 72}, - {15, 0x02, 72}, - {24, 0x02, 72}, - {31, 0x02, 72}, - {41, 0x02, 72}, - {56, 0x03, 72}, - }, - /* 46 */ - { - {2, 0x02, 73}, - {9, 0x02, 73}, - {23, 0x02, 73}, - {40, 0x03, 73}, - {2, 0x02, 74}, - {9, 0x02, 74}, - {23, 0x02, 74}, - {40, 0x03, 74}, - {2, 0x02, 75}, - {9, 0x02, 75}, - {23, 0x02, 75}, - {40, 0x03, 75}, - {2, 0x02, 76}, - {9, 0x02, 76}, - {23, 0x02, 76}, - {40, 0x03, 76}, - }, - /* 47 */ - { - {3, 0x02, 73}, - {6, 0x02, 73}, - {10, 0x02, 73}, - {15, 0x02, 73}, - {24, 0x02, 73}, - {31, 0x02, 73}, - {41, 0x02, 73}, - {56, 0x03, 73}, - {3, 0x02, 74}, - {6, 0x02, 74}, - {10, 0x02, 74}, - {15, 0x02, 74}, - {24, 0x02, 74}, - {31, 0x02, 74}, - {41, 0x02, 74}, - {56, 0x03, 74}, - }, - /* 48 */ - { - {3, 0x02, 75}, - {6, 0x02, 75}, - {10, 0x02, 75}, - {15, 0x02, 75}, - {24, 0x02, 75}, - {31, 0x02, 75}, - {41, 0x02, 75}, - {56, 0x03, 75}, - {3, 0x02, 76}, - {6, 0x02, 76}, - {10, 0x02, 76}, - {15, 0x02, 76}, - {24, 0x02, 76}, - {31, 0x02, 76}, - {41, 0x02, 76}, - {56, 0x03, 76}, - }, - /* 49 */ - { - {1, 0x02, 77}, - {22, 0x03, 77}, - {1, 0x02, 78}, - {22, 0x03, 78}, - {1, 0x02, 79}, - {22, 0x03, 79}, - {1, 0x02, 80}, - {22, 0x03, 80}, - {1, 0x02, 81}, - {22, 0x03, 81}, - {1, 0x02, 82}, - {22, 0x03, 82}, - {1, 0x02, 83}, - {22, 0x03, 83}, - {1, 0x02, 84}, - {22, 0x03, 84}, - }, - /* 50 */ - { - {2, 0x02, 77}, - {9, 0x02, 77}, - {23, 0x02, 77}, - {40, 0x03, 77}, - {2, 0x02, 78}, - {9, 0x02, 78}, - {23, 0x02, 78}, - {40, 0x03, 78}, - {2, 0x02, 79}, - {9, 0x02, 79}, - {23, 0x02, 79}, - {40, 0x03, 79}, - {2, 0x02, 80}, - {9, 0x02, 80}, - {23, 0x02, 80}, - {40, 0x03, 80}, - }, - /* 51 */ - { - {3, 0x02, 77}, - {6, 0x02, 77}, - {10, 0x02, 77}, - {15, 0x02, 77}, - {24, 0x02, 77}, - {31, 0x02, 77}, - {41, 0x02, 77}, - {56, 0x03, 77}, - {3, 0x02, 78}, - {6, 0x02, 78}, - {10, 0x02, 78}, - {15, 0x02, 78}, - {24, 0x02, 78}, - {31, 0x02, 78}, - {41, 0x02, 78}, - {56, 0x03, 78}, - }, - /* 52 */ - { - {3, 0x02, 79}, - {6, 0x02, 79}, - {10, 0x02, 79}, - {15, 0x02, 79}, - {24, 0x02, 79}, - {31, 0x02, 79}, - {41, 0x02, 79}, - {56, 0x03, 79}, - {3, 0x02, 80}, - {6, 0x02, 80}, - {10, 0x02, 80}, - {15, 0x02, 80}, - {24, 0x02, 80}, - {31, 0x02, 80}, - {41, 0x02, 80}, - {56, 0x03, 80}, - }, - /* 53 */ - { - {2, 0x02, 81}, - {9, 0x02, 81}, - {23, 0x02, 81}, - {40, 0x03, 81}, - {2, 0x02, 82}, - {9, 0x02, 82}, - {23, 0x02, 82}, - {40, 0x03, 82}, - {2, 0x02, 83}, - {9, 0x02, 83}, - {23, 0x02, 83}, - {40, 0x03, 83}, - {2, 0x02, 84}, - {9, 0x02, 84}, - {23, 0x02, 84}, - {40, 0x03, 84}, - }, - /* 54 */ - { - {3, 0x02, 81}, - {6, 0x02, 81}, - {10, 0x02, 81}, - {15, 0x02, 81}, - {24, 0x02, 81}, - {31, 0x02, 81}, - {41, 0x02, 81}, - {56, 0x03, 81}, - {3, 0x02, 82}, - {6, 0x02, 82}, - {10, 0x02, 82}, - {15, 0x02, 82}, - {24, 0x02, 82}, - {31, 0x02, 82}, - {41, 0x02, 82}, - {56, 0x03, 82}, - }, - /* 55 */ - { - {3, 0x02, 83}, - {6, 0x02, 83}, - {10, 0x02, 83}, - {15, 0x02, 83}, - {24, 0x02, 83}, - {31, 0x02, 83}, - {41, 0x02, 83}, - {56, 0x03, 83}, - {3, 0x02, 84}, - {6, 0x02, 84}, - {10, 0x02, 84}, - {15, 0x02, 84}, - {24, 0x02, 84}, - {31, 0x02, 84}, - {41, 0x02, 84}, - {56, 0x03, 84}, - }, - /* 56 */ - { - {0, 0x03, 85}, - {0, 0x03, 86}, - {0, 0x03, 87}, - {0, 0x03, 89}, - {0, 0x03, 106}, - {0, 0x03, 107}, - {0, 0x03, 113}, - {0, 0x03, 118}, - {0, 0x03, 119}, - {0, 0x03, 120}, - {0, 0x03, 121}, - {0, 0x03, 122}, - {70, 0x00, 0}, - {71, 0x00, 0}, - {73, 0x00, 0}, - {74, 0x01, 0}, - }, - /* 57 */ - { - {1, 0x02, 85}, - {22, 0x03, 85}, - {1, 0x02, 86}, - {22, 0x03, 86}, - {1, 0x02, 87}, - {22, 0x03, 87}, - {1, 0x02, 89}, - {22, 0x03, 89}, - {1, 0x02, 106}, - {22, 0x03, 106}, - {1, 0x02, 107}, - {22, 0x03, 107}, - {1, 0x02, 113}, - {22, 0x03, 113}, - {1, 0x02, 118}, - {22, 0x03, 118}, - }, - /* 58 */ - { - {2, 0x02, 85}, - {9, 0x02, 85}, - {23, 0x02, 85}, - {40, 0x03, 85}, - {2, 0x02, 86}, - {9, 0x02, 86}, - {23, 0x02, 86}, - {40, 0x03, 86}, - {2, 0x02, 87}, - {9, 0x02, 87}, - {23, 0x02, 87}, - {40, 0x03, 87}, - {2, 0x02, 89}, - {9, 0x02, 89}, - {23, 0x02, 89}, - {40, 0x03, 89}, - }, - /* 59 */ - { - {3, 0x02, 85}, - {6, 0x02, 85}, - {10, 0x02, 85}, - {15, 0x02, 85}, - {24, 0x02, 85}, - {31, 0x02, 85}, - {41, 0x02, 85}, - {56, 0x03, 85}, - {3, 0x02, 86}, - {6, 0x02, 86}, - {10, 0x02, 86}, - {15, 0x02, 86}, - {24, 0x02, 86}, - {31, 0x02, 86}, - {41, 0x02, 86}, - {56, 0x03, 86}, - }, - /* 60 */ - { - {3, 0x02, 87}, - {6, 0x02, 87}, - {10, 0x02, 87}, - {15, 0x02, 87}, - {24, 0x02, 87}, - {31, 0x02, 87}, - {41, 0x02, 87}, - {56, 0x03, 87}, - {3, 0x02, 89}, - {6, 0x02, 89}, - {10, 0x02, 89}, - {15, 0x02, 89}, - {24, 0x02, 89}, - {31, 0x02, 89}, - {41, 0x02, 89}, - {56, 0x03, 89}, - }, - /* 61 */ - { - {2, 0x02, 106}, - {9, 0x02, 106}, - {23, 0x02, 106}, - {40, 0x03, 106}, - {2, 0x02, 107}, - {9, 0x02, 107}, - {23, 0x02, 107}, - {40, 0x03, 107}, - {2, 0x02, 113}, - {9, 0x02, 113}, - {23, 0x02, 113}, - {40, 0x03, 113}, - {2, 0x02, 118}, - {9, 0x02, 118}, - {23, 0x02, 118}, - {40, 0x03, 118}, - }, - /* 62 */ - { - {3, 0x02, 106}, - {6, 0x02, 106}, - {10, 0x02, 106}, - {15, 0x02, 106}, - {24, 0x02, 106}, - {31, 0x02, 106}, - {41, 0x02, 106}, - {56, 0x03, 106}, - {3, 0x02, 107}, - {6, 0x02, 107}, - {10, 0x02, 107}, - {15, 0x02, 107}, - {24, 0x02, 107}, - {31, 0x02, 107}, - {41, 0x02, 107}, - {56, 0x03, 107}, - }, - /* 63 */ - { - {3, 0x02, 113}, - {6, 0x02, 113}, - {10, 0x02, 113}, - {15, 0x02, 113}, - {24, 0x02, 113}, - {31, 0x02, 113}, - {41, 0x02, 113}, - {56, 0x03, 113}, - {3, 0x02, 118}, - {6, 0x02, 118}, - {10, 0x02, 118}, - {15, 0x02, 118}, - {24, 0x02, 118}, - {31, 0x02, 118}, - {41, 0x02, 118}, - {56, 0x03, 118}, - }, - /* 64 */ - { - {1, 0x02, 119}, - {22, 0x03, 119}, - {1, 0x02, 120}, - {22, 0x03, 120}, - {1, 0x02, 121}, - {22, 0x03, 121}, - {1, 0x02, 122}, - {22, 0x03, 122}, - {0, 0x03, 38}, - {0, 0x03, 42}, - {0, 0x03, 44}, - {0, 0x03, 59}, - {0, 0x03, 88}, - {0, 0x03, 90}, - {75, 0x00, 0}, - {78, 0x00, 0}, - }, - /* 65 */ - { - {2, 0x02, 119}, - {9, 0x02, 119}, - {23, 0x02, 119}, - {40, 0x03, 119}, - {2, 0x02, 120}, - {9, 0x02, 120}, - {23, 0x02, 120}, - {40, 0x03, 120}, - {2, 0x02, 121}, - {9, 0x02, 121}, - {23, 0x02, 121}, - {40, 0x03, 121}, - {2, 0x02, 122}, - {9, 0x02, 122}, - {23, 0x02, 122}, - {40, 0x03, 122}, - }, - /* 66 */ - { - {3, 0x02, 119}, - {6, 0x02, 119}, - {10, 0x02, 119}, - {15, 0x02, 119}, - {24, 0x02, 119}, - {31, 0x02, 119}, - {41, 0x02, 119}, - {56, 0x03, 119}, - {3, 0x02, 120}, - {6, 0x02, 120}, - {10, 0x02, 120}, - {15, 0x02, 120}, - {24, 0x02, 120}, - {31, 0x02, 120}, - {41, 0x02, 120}, - {56, 0x03, 120}, - }, - /* 67 */ - { - {3, 0x02, 121}, - {6, 0x02, 121}, - {10, 0x02, 121}, - {15, 0x02, 121}, - {24, 0x02, 121}, - {31, 0x02, 121}, - {41, 0x02, 121}, - {56, 0x03, 121}, - {3, 0x02, 122}, - {6, 0x02, 122}, - {10, 0x02, 122}, - {15, 0x02, 122}, - {24, 0x02, 122}, - {31, 0x02, 122}, - {41, 0x02, 122}, - {56, 0x03, 122}, - }, - /* 68 */ - { - {1, 0x02, 38}, - {22, 0x03, 38}, - {1, 0x02, 42}, - {22, 0x03, 42}, - {1, 0x02, 44}, - {22, 0x03, 44}, - {1, 0x02, 59}, - {22, 0x03, 59}, - {1, 0x02, 88}, - {22, 0x03, 88}, - {1, 0x02, 90}, - {22, 0x03, 90}, - {76, 0x00, 0}, - {77, 0x00, 0}, - {79, 0x00, 0}, - {81, 0x00, 0}, - }, - /* 69 */ - { - {2, 0x02, 38}, - {9, 0x02, 38}, - {23, 0x02, 38}, - {40, 0x03, 38}, - {2, 0x02, 42}, - {9, 0x02, 42}, - {23, 0x02, 42}, - {40, 0x03, 42}, - {2, 0x02, 44}, - {9, 0x02, 44}, - {23, 0x02, 44}, - {40, 0x03, 44}, - {2, 0x02, 59}, - {9, 0x02, 59}, - {23, 0x02, 59}, - {40, 0x03, 59}, - }, - /* 70 */ - { - {3, 0x02, 38}, - {6, 0x02, 38}, - {10, 0x02, 38}, - {15, 0x02, 38}, - {24, 0x02, 38}, - {31, 0x02, 38}, - {41, 0x02, 38}, - {56, 0x03, 38}, - {3, 0x02, 42}, - {6, 0x02, 42}, - {10, 0x02, 42}, - {15, 0x02, 42}, - {24, 0x02, 42}, - {31, 0x02, 42}, - {41, 0x02, 42}, - {56, 0x03, 42}, - }, - /* 71 */ - { - {3, 0x02, 44}, - {6, 0x02, 44}, - {10, 0x02, 44}, - {15, 0x02, 44}, - {24, 0x02, 44}, - {31, 0x02, 44}, - {41, 0x02, 44}, - {56, 0x03, 44}, - {3, 0x02, 59}, - {6, 0x02, 59}, - {10, 0x02, 59}, - {15, 0x02, 59}, - {24, 0x02, 59}, - {31, 0x02, 59}, - {41, 0x02, 59}, - {56, 0x03, 59}, - }, - /* 72 */ - { - {2, 0x02, 88}, - {9, 0x02, 88}, - {23, 0x02, 88}, - {40, 0x03, 88}, - {2, 0x02, 90}, - {9, 0x02, 90}, - {23, 0x02, 90}, - {40, 0x03, 90}, - {0, 0x03, 33}, - {0, 0x03, 34}, - {0, 0x03, 40}, - {0, 0x03, 41}, - {0, 0x03, 63}, - {80, 0x00, 0}, - {82, 0x00, 0}, - {84, 0x00, 0}, - }, - /* 73 */ - { - {3, 0x02, 88}, - {6, 0x02, 88}, - {10, 0x02, 88}, - {15, 0x02, 88}, - {24, 0x02, 88}, - {31, 0x02, 88}, - {41, 0x02, 88}, - {56, 0x03, 88}, - {3, 0x02, 90}, - {6, 0x02, 90}, - {10, 0x02, 90}, - {15, 0x02, 90}, - {24, 0x02, 90}, - {31, 0x02, 90}, - {41, 0x02, 90}, - {56, 0x03, 90}, - }, - /* 74 */ - { - {1, 0x02, 33}, - {22, 0x03, 33}, - {1, 0x02, 34}, - {22, 0x03, 34}, - {1, 0x02, 40}, - {22, 0x03, 40}, - {1, 0x02, 41}, - {22, 0x03, 41}, - {1, 0x02, 63}, - {22, 0x03, 63}, - {0, 0x03, 39}, - {0, 0x03, 43}, - {0, 0x03, 124}, - {83, 0x00, 0}, - {85, 0x00, 0}, - {88, 0x00, 0}, - }, - /* 75 */ - { - {2, 0x02, 33}, - {9, 0x02, 33}, - {23, 0x02, 33}, - {40, 0x03, 33}, - {2, 0x02, 34}, - {9, 0x02, 34}, - {23, 0x02, 34}, - {40, 0x03, 34}, - {2, 0x02, 40}, - {9, 0x02, 40}, - {23, 0x02, 40}, - {40, 0x03, 40}, - {2, 0x02, 41}, - {9, 0x02, 41}, - {23, 0x02, 41}, - {40, 0x03, 41}, - }, - /* 76 */ - { - {3, 0x02, 33}, - {6, 0x02, 33}, - {10, 0x02, 33}, - {15, 0x02, 33}, - {24, 0x02, 33}, - {31, 0x02, 33}, - {41, 0x02, 33}, - {56, 0x03, 33}, - {3, 0x02, 34}, - {6, 0x02, 34}, - {10, 0x02, 34}, - {15, 0x02, 34}, - {24, 0x02, 34}, - {31, 0x02, 34}, - {41, 0x02, 34}, - {56, 0x03, 34}, - }, - /* 77 */ - { - {3, 0x02, 40}, - {6, 0x02, 40}, - {10, 0x02, 40}, - {15, 0x02, 40}, - {24, 0x02, 40}, - {31, 0x02, 40}, - {41, 0x02, 40}, - {56, 0x03, 40}, - {3, 0x02, 41}, - {6, 0x02, 41}, - {10, 0x02, 41}, - {15, 0x02, 41}, - {24, 0x02, 41}, - {31, 0x02, 41}, - {41, 0x02, 41}, - {56, 0x03, 41}, - }, - /* 78 */ - { - {2, 0x02, 63}, - {9, 0x02, 63}, - {23, 0x02, 63}, - {40, 0x03, 63}, - {1, 0x02, 39}, - {22, 0x03, 39}, - {1, 0x02, 43}, - {22, 0x03, 43}, - {1, 0x02, 124}, - {22, 0x03, 124}, - {0, 0x03, 35}, - {0, 0x03, 62}, - {86, 0x00, 0}, - {87, 0x00, 0}, - {89, 0x00, 0}, - {90, 0x00, 0}, - }, - /* 79 */ - { - {3, 0x02, 63}, - {6, 0x02, 63}, - {10, 0x02, 63}, - {15, 0x02, 63}, - {24, 0x02, 63}, - {31, 0x02, 63}, - {41, 0x02, 63}, - {56, 0x03, 63}, - {2, 0x02, 39}, - {9, 0x02, 39}, - {23, 0x02, 39}, - {40, 0x03, 39}, - {2, 0x02, 43}, - {9, 0x02, 43}, - {23, 0x02, 43}, - {40, 0x03, 43}, - }, - /* 80 */ - { - {3, 0x02, 39}, - {6, 0x02, 39}, - {10, 0x02, 39}, - {15, 0x02, 39}, - {24, 0x02, 39}, - {31, 0x02, 39}, - {41, 0x02, 39}, - {56, 0x03, 39}, - {3, 0x02, 43}, - {6, 0x02, 43}, - {10, 0x02, 43}, - {15, 0x02, 43}, - {24, 0x02, 43}, - {31, 0x02, 43}, - {41, 0x02, 43}, - {56, 0x03, 43}, - }, - /* 81 */ - { - {2, 0x02, 124}, - {9, 0x02, 124}, - {23, 0x02, 124}, - {40, 0x03, 124}, - {1, 0x02, 35}, - {22, 0x03, 35}, - {1, 0x02, 62}, - {22, 0x03, 62}, - {0, 0x03, 0}, - {0, 0x03, 36}, - {0, 0x03, 64}, - {0, 0x03, 91}, - {0, 0x03, 93}, - {0, 0x03, 126}, - {91, 0x00, 0}, - {92, 0x00, 0}, - }, - /* 82 */ - { - {3, 0x02, 124}, - {6, 0x02, 124}, - {10, 0x02, 124}, - {15, 0x02, 124}, - {24, 0x02, 124}, - {31, 0x02, 124}, - {41, 0x02, 124}, - {56, 0x03, 124}, - {2, 0x02, 35}, - {9, 0x02, 35}, - {23, 0x02, 35}, - {40, 0x03, 35}, - {2, 0x02, 62}, - {9, 0x02, 62}, - {23, 0x02, 62}, - {40, 0x03, 62}, - }, - /* 83 */ - { - {3, 0x02, 35}, - {6, 0x02, 35}, - {10, 0x02, 35}, - {15, 0x02, 35}, - {24, 0x02, 35}, - {31, 0x02, 35}, - {41, 0x02, 35}, - {56, 0x03, 35}, - {3, 0x02, 62}, - {6, 0x02, 62}, - {10, 0x02, 62}, - {15, 0x02, 62}, - {24, 0x02, 62}, - {31, 0x02, 62}, - {41, 0x02, 62}, - {56, 0x03, 62}, - }, - /* 84 */ - { - {1, 0x02, 0}, - {22, 0x03, 0}, - {1, 0x02, 36}, - {22, 0x03, 36}, - {1, 0x02, 64}, - {22, 0x03, 64}, - {1, 0x02, 91}, - {22, 0x03, 91}, - {1, 0x02, 93}, - {22, 0x03, 93}, - {1, 0x02, 126}, - {22, 0x03, 126}, - {0, 0x03, 94}, - {0, 0x03, 125}, - {93, 0x00, 0}, - {94, 0x00, 0}, - }, - /* 85 */ - { - {2, 0x02, 0}, - {9, 0x02, 0}, - {23, 0x02, 0}, - {40, 0x03, 0}, - {2, 0x02, 36}, - {9, 0x02, 36}, - {23, 0x02, 36}, - {40, 0x03, 36}, - {2, 0x02, 64}, - {9, 0x02, 64}, - {23, 0x02, 64}, - {40, 0x03, 64}, - {2, 0x02, 91}, - {9, 0x02, 91}, - {23, 0x02, 91}, - {40, 0x03, 91}, - }, - /* 86 */ - { - {3, 0x02, 0}, - {6, 0x02, 0}, - {10, 0x02, 0}, - {15, 0x02, 0}, - {24, 0x02, 0}, - {31, 0x02, 0}, - {41, 0x02, 0}, - {56, 0x03, 0}, - {3, 0x02, 36}, - {6, 0x02, 36}, - {10, 0x02, 36}, - {15, 0x02, 36}, - {24, 0x02, 36}, - {31, 0x02, 36}, - {41, 0x02, 36}, - {56, 0x03, 36}, - }, - /* 87 */ - { - {3, 0x02, 64}, - {6, 0x02, 64}, - {10, 0x02, 64}, - {15, 0x02, 64}, - {24, 0x02, 64}, - {31, 0x02, 64}, - {41, 0x02, 64}, - {56, 0x03, 64}, - {3, 0x02, 91}, - {6, 0x02, 91}, - {10, 0x02, 91}, - {15, 0x02, 91}, - {24, 0x02, 91}, - {31, 0x02, 91}, - {41, 0x02, 91}, - {56, 0x03, 91}, - }, - /* 88 */ - { - {2, 0x02, 93}, - {9, 0x02, 93}, - {23, 0x02, 93}, - {40, 0x03, 93}, - {2, 0x02, 126}, - {9, 0x02, 126}, - {23, 0x02, 126}, - {40, 0x03, 126}, - {1, 0x02, 94}, - {22, 0x03, 94}, - {1, 0x02, 125}, - {22, 0x03, 125}, - {0, 0x03, 60}, - {0, 0x03, 96}, - {0, 0x03, 123}, - {95, 0x00, 0}, - }, - /* 89 */ - { - {3, 0x02, 93}, - {6, 0x02, 93}, - {10, 0x02, 93}, - {15, 0x02, 93}, - {24, 0x02, 93}, - {31, 0x02, 93}, - {41, 0x02, 93}, - {56, 0x03, 93}, - {3, 0x02, 126}, - {6, 0x02, 126}, - {10, 0x02, 126}, - {15, 0x02, 126}, - {24, 0x02, 126}, - {31, 0x02, 126}, - {41, 0x02, 126}, - {56, 0x03, 126}, - }, - /* 90 */ - { - {2, 0x02, 94}, - {9, 0x02, 94}, - {23, 0x02, 94}, - {40, 0x03, 94}, - {2, 0x02, 125}, - {9, 0x02, 125}, - {23, 0x02, 125}, - {40, 0x03, 125}, - {1, 0x02, 60}, - {22, 0x03, 60}, - {1, 0x02, 96}, - {22, 0x03, 96}, - {1, 0x02, 123}, - {22, 0x03, 123}, - {96, 0x00, 0}, - {110, 0x00, 0}, - }, - /* 91 */ - { - {3, 0x02, 94}, - {6, 0x02, 94}, - {10, 0x02, 94}, - {15, 0x02, 94}, - {24, 0x02, 94}, - {31, 0x02, 94}, - {41, 0x02, 94}, - {56, 0x03, 94}, - {3, 0x02, 125}, - {6, 0x02, 125}, - {10, 0x02, 125}, - {15, 0x02, 125}, - {24, 0x02, 125}, - {31, 0x02, 125}, - {41, 0x02, 125}, - {56, 0x03, 125}, - }, - /* 92 */ - { - {2, 0x02, 60}, - {9, 0x02, 60}, - {23, 0x02, 60}, - {40, 0x03, 60}, - {2, 0x02, 96}, - {9, 0x02, 96}, - {23, 0x02, 96}, - {40, 0x03, 96}, - {2, 0x02, 123}, - {9, 0x02, 123}, - {23, 0x02, 123}, - {40, 0x03, 123}, - {97, 0x00, 0}, - {101, 0x00, 0}, - {111, 0x00, 0}, - {133, 0x00, 0}, - }, - /* 93 */ - { - {3, 0x02, 60}, - {6, 0x02, 60}, - {10, 0x02, 60}, - {15, 0x02, 60}, - {24, 0x02, 60}, - {31, 0x02, 60}, - {41, 0x02, 60}, - {56, 0x03, 60}, - {3, 0x02, 96}, - {6, 0x02, 96}, - {10, 0x02, 96}, - {15, 0x02, 96}, - {24, 0x02, 96}, - {31, 0x02, 96}, - {41, 0x02, 96}, - {56, 0x03, 96}, - }, - /* 94 */ - { - {3, 0x02, 123}, - {6, 0x02, 123}, - {10, 0x02, 123}, - {15, 0x02, 123}, - {24, 0x02, 123}, - {31, 0x02, 123}, - {41, 0x02, 123}, - {56, 0x03, 123}, - {98, 0x00, 0}, - {99, 0x00, 0}, - {102, 0x00, 0}, - {105, 0x00, 0}, - {112, 0x00, 0}, - {119, 0x00, 0}, - {134, 0x00, 0}, - {153, 0x00, 0}, - }, - /* 95 */ - { - {0, 0x03, 92}, - {0, 0x03, 195}, - {0, 0x03, 208}, - {100, 0x00, 0}, - {103, 0x00, 0}, - {104, 0x00, 0}, - {106, 0x00, 0}, - {107, 0x00, 0}, - {113, 0x00, 0}, - {116, 0x00, 0}, - {120, 0x00, 0}, - {126, 0x00, 0}, - {135, 0x00, 0}, - {142, 0x00, 0}, - {154, 0x00, 0}, - {169, 0x00, 0}, - }, - /* 96 */ - { - {1, 0x02, 92}, - {22, 0x03, 92}, - {1, 0x02, 195}, - {22, 0x03, 195}, - {1, 0x02, 208}, - {22, 0x03, 208}, - {0, 0x03, 128}, - {0, 0x03, 130}, - {0, 0x03, 131}, - {0, 0x03, 162}, - {0, 0x03, 184}, - {0, 0x03, 194}, - {0, 0x03, 224}, - {0, 0x03, 226}, - {108, 0x00, 0}, - {109, 0x00, 0}, - }, - /* 97 */ - { - {2, 0x02, 92}, - {9, 0x02, 92}, - {23, 0x02, 92}, - {40, 0x03, 92}, - {2, 0x02, 195}, - {9, 0x02, 195}, - {23, 0x02, 195}, - {40, 0x03, 195}, - {2, 0x02, 208}, - {9, 0x02, 208}, - {23, 0x02, 208}, - {40, 0x03, 208}, - {1, 0x02, 128}, - {22, 0x03, 128}, - {1, 0x02, 130}, - {22, 0x03, 130}, - }, - /* 98 */ - { - {3, 0x02, 92}, - {6, 0x02, 92}, - {10, 0x02, 92}, - {15, 0x02, 92}, - {24, 0x02, 92}, - {31, 0x02, 92}, - {41, 0x02, 92}, - {56, 0x03, 92}, - {3, 0x02, 195}, - {6, 0x02, 195}, - {10, 0x02, 195}, - {15, 0x02, 195}, - {24, 0x02, 195}, - {31, 0x02, 195}, - {41, 0x02, 195}, - {56, 0x03, 195}, - }, - /* 99 */ - { - {3, 0x02, 208}, - {6, 0x02, 208}, - {10, 0x02, 208}, - {15, 0x02, 208}, - {24, 0x02, 208}, - {31, 0x02, 208}, - {41, 0x02, 208}, - {56, 0x03, 208}, - {2, 0x02, 128}, - {9, 0x02, 128}, - {23, 0x02, 128}, - {40, 0x03, 128}, - {2, 0x02, 130}, - {9, 0x02, 130}, - {23, 0x02, 130}, - {40, 0x03, 130}, - }, - /* 100 */ - { - {3, 0x02, 128}, - {6, 0x02, 128}, - {10, 0x02, 128}, - {15, 0x02, 128}, - {24, 0x02, 128}, - {31, 0x02, 128}, - {41, 0x02, 128}, - {56, 0x03, 128}, - {3, 0x02, 130}, - {6, 0x02, 130}, - {10, 0x02, 130}, - {15, 0x02, 130}, - {24, 0x02, 130}, - {31, 0x02, 130}, - {41, 0x02, 130}, - {56, 0x03, 130}, - }, - /* 101 */ - { - {1, 0x02, 131}, - {22, 0x03, 131}, - {1, 0x02, 162}, - {22, 0x03, 162}, - {1, 0x02, 184}, - {22, 0x03, 184}, - {1, 0x02, 194}, - {22, 0x03, 194}, - {1, 0x02, 224}, - {22, 0x03, 224}, - {1, 0x02, 226}, - {22, 0x03, 226}, - {0, 0x03, 153}, - {0, 0x03, 161}, - {0, 0x03, 167}, - {0, 0x03, 172}, - }, - /* 102 */ - { - {2, 0x02, 131}, - {9, 0x02, 131}, - {23, 0x02, 131}, - {40, 0x03, 131}, - {2, 0x02, 162}, - {9, 0x02, 162}, - {23, 0x02, 162}, - {40, 0x03, 162}, - {2, 0x02, 184}, - {9, 0x02, 184}, - {23, 0x02, 184}, - {40, 0x03, 184}, - {2, 0x02, 194}, - {9, 0x02, 194}, - {23, 0x02, 194}, - {40, 0x03, 194}, - }, - /* 103 */ - { - {3, 0x02, 131}, - {6, 0x02, 131}, - {10, 0x02, 131}, - {15, 0x02, 131}, - {24, 0x02, 131}, - {31, 0x02, 131}, - {41, 0x02, 131}, - {56, 0x03, 131}, - {3, 0x02, 162}, - {6, 0x02, 162}, - {10, 0x02, 162}, - {15, 0x02, 162}, - {24, 0x02, 162}, - {31, 0x02, 162}, - {41, 0x02, 162}, - {56, 0x03, 162}, - }, - /* 104 */ - { - {3, 0x02, 184}, - {6, 0x02, 184}, - {10, 0x02, 184}, - {15, 0x02, 184}, - {24, 0x02, 184}, - {31, 0x02, 184}, - {41, 0x02, 184}, - {56, 0x03, 184}, - {3, 0x02, 194}, - {6, 0x02, 194}, - {10, 0x02, 194}, - {15, 0x02, 194}, - {24, 0x02, 194}, - {31, 0x02, 194}, - {41, 0x02, 194}, - {56, 0x03, 194}, - }, - /* 105 */ - { - {2, 0x02, 224}, - {9, 0x02, 224}, - {23, 0x02, 224}, - {40, 0x03, 224}, - {2, 0x02, 226}, - {9, 0x02, 226}, - {23, 0x02, 226}, - {40, 0x03, 226}, - {1, 0x02, 153}, - {22, 0x03, 153}, - {1, 0x02, 161}, - {22, 0x03, 161}, - {1, 0x02, 167}, - {22, 0x03, 167}, - {1, 0x02, 172}, - {22, 0x03, 172}, - }, - /* 106 */ - { - {3, 0x02, 224}, - {6, 0x02, 224}, - {10, 0x02, 224}, - {15, 0x02, 224}, - {24, 0x02, 224}, - {31, 0x02, 224}, - {41, 0x02, 224}, - {56, 0x03, 224}, - {3, 0x02, 226}, - {6, 0x02, 226}, - {10, 0x02, 226}, - {15, 0x02, 226}, - {24, 0x02, 226}, - {31, 0x02, 226}, - {41, 0x02, 226}, - {56, 0x03, 226}, - }, - /* 107 */ - { - {2, 0x02, 153}, - {9, 0x02, 153}, - {23, 0x02, 153}, - {40, 0x03, 153}, - {2, 0x02, 161}, - {9, 0x02, 161}, - {23, 0x02, 161}, - {40, 0x03, 161}, - {2, 0x02, 167}, - {9, 0x02, 167}, - {23, 0x02, 167}, - {40, 0x03, 167}, - {2, 0x02, 172}, - {9, 0x02, 172}, - {23, 0x02, 172}, - {40, 0x03, 172}, - }, - /* 108 */ - { - {3, 0x02, 153}, - {6, 0x02, 153}, - {10, 0x02, 153}, - {15, 0x02, 153}, - {24, 0x02, 153}, - {31, 0x02, 153}, - {41, 0x02, 153}, - {56, 0x03, 153}, - {3, 0x02, 161}, - {6, 0x02, 161}, - {10, 0x02, 161}, - {15, 0x02, 161}, - {24, 0x02, 161}, - {31, 0x02, 161}, - {41, 0x02, 161}, - {56, 0x03, 161}, - }, - /* 109 */ - { - {3, 0x02, 167}, - {6, 0x02, 167}, - {10, 0x02, 167}, - {15, 0x02, 167}, - {24, 0x02, 167}, - {31, 0x02, 167}, - {41, 0x02, 167}, - {56, 0x03, 167}, - {3, 0x02, 172}, - {6, 0x02, 172}, - {10, 0x02, 172}, - {15, 0x02, 172}, - {24, 0x02, 172}, - {31, 0x02, 172}, - {41, 0x02, 172}, - {56, 0x03, 172}, - }, - /* 110 */ - { - {114, 0x00, 0}, - {115, 0x00, 0}, - {117, 0x00, 0}, - {118, 0x00, 0}, - {121, 0x00, 0}, - {123, 0x00, 0}, - {127, 0x00, 0}, - {130, 0x00, 0}, - {136, 0x00, 0}, - {139, 0x00, 0}, - {143, 0x00, 0}, - {146, 0x00, 0}, - {155, 0x00, 0}, - {162, 0x00, 0}, - {170, 0x00, 0}, - {180, 0x00, 0}, - }, - /* 111 */ - { - {0, 0x03, 176}, - {0, 0x03, 177}, - {0, 0x03, 179}, - {0, 0x03, 209}, - {0, 0x03, 216}, - {0, 0x03, 217}, - {0, 0x03, 227}, - {0, 0x03, 229}, - {0, 0x03, 230}, - {122, 0x00, 0}, - {124, 0x00, 0}, - {125, 0x00, 0}, - {128, 0x00, 0}, - {129, 0x00, 0}, - {131, 0x00, 0}, - {132, 0x00, 0}, - }, - /* 112 */ - { - {1, 0x02, 176}, - {22, 0x03, 176}, - {1, 0x02, 177}, - {22, 0x03, 177}, - {1, 0x02, 179}, - {22, 0x03, 179}, - {1, 0x02, 209}, - {22, 0x03, 209}, - {1, 0x02, 216}, - {22, 0x03, 216}, - {1, 0x02, 217}, - {22, 0x03, 217}, - {1, 0x02, 227}, - {22, 0x03, 227}, - {1, 0x02, 229}, - {22, 0x03, 229}, - }, - /* 113 */ - { - {2, 0x02, 176}, - {9, 0x02, 176}, - {23, 0x02, 176}, - {40, 0x03, 176}, - {2, 0x02, 177}, - {9, 0x02, 177}, - {23, 0x02, 177}, - {40, 0x03, 177}, - {2, 0x02, 179}, - {9, 0x02, 179}, - {23, 0x02, 179}, - {40, 0x03, 179}, - {2, 0x02, 209}, - {9, 0x02, 209}, - {23, 0x02, 209}, - {40, 0x03, 209}, - }, - /* 114 */ - { - {3, 0x02, 176}, - {6, 0x02, 176}, - {10, 0x02, 176}, - {15, 0x02, 176}, - {24, 0x02, 176}, - {31, 0x02, 176}, - {41, 0x02, 176}, - {56, 0x03, 176}, - {3, 0x02, 177}, - {6, 0x02, 177}, - {10, 0x02, 177}, - {15, 0x02, 177}, - {24, 0x02, 177}, - {31, 0x02, 177}, - {41, 0x02, 177}, - {56, 0x03, 177}, - }, - /* 115 */ - { - {3, 0x02, 179}, - {6, 0x02, 179}, - {10, 0x02, 179}, - {15, 0x02, 179}, - {24, 0x02, 179}, - {31, 0x02, 179}, - {41, 0x02, 179}, - {56, 0x03, 179}, - {3, 0x02, 209}, - {6, 0x02, 209}, - {10, 0x02, 209}, - {15, 0x02, 209}, - {24, 0x02, 209}, - {31, 0x02, 209}, - {41, 0x02, 209}, - {56, 0x03, 209}, - }, - /* 116 */ - { - {2, 0x02, 216}, - {9, 0x02, 216}, - {23, 0x02, 216}, - {40, 0x03, 216}, - {2, 0x02, 217}, - {9, 0x02, 217}, - {23, 0x02, 217}, - {40, 0x03, 217}, - {2, 0x02, 227}, - {9, 0x02, 227}, - {23, 0x02, 227}, - {40, 0x03, 227}, - {2, 0x02, 229}, - {9, 0x02, 229}, - {23, 0x02, 229}, - {40, 0x03, 229}, - }, - /* 117 */ - { - {3, 0x02, 216}, - {6, 0x02, 216}, - {10, 0x02, 216}, - {15, 0x02, 216}, - {24, 0x02, 216}, - {31, 0x02, 216}, - {41, 0x02, 216}, - {56, 0x03, 216}, - {3, 0x02, 217}, - {6, 0x02, 217}, - {10, 0x02, 217}, - {15, 0x02, 217}, - {24, 0x02, 217}, - {31, 0x02, 217}, - {41, 0x02, 217}, - {56, 0x03, 217}, - }, - /* 118 */ - { - {3, 0x02, 227}, - {6, 0x02, 227}, - {10, 0x02, 227}, - {15, 0x02, 227}, - {24, 0x02, 227}, - {31, 0x02, 227}, - {41, 0x02, 227}, - {56, 0x03, 227}, - {3, 0x02, 229}, - {6, 0x02, 229}, - {10, 0x02, 229}, - {15, 0x02, 229}, - {24, 0x02, 229}, - {31, 0x02, 229}, - {41, 0x02, 229}, - {56, 0x03, 229}, - }, - /* 119 */ - { - {1, 0x02, 230}, - {22, 0x03, 230}, - {0, 0x03, 129}, - {0, 0x03, 132}, - {0, 0x03, 133}, - {0, 0x03, 134}, - {0, 0x03, 136}, - {0, 0x03, 146}, - {0, 0x03, 154}, - {0, 0x03, 156}, - {0, 0x03, 160}, - {0, 0x03, 163}, - {0, 0x03, 164}, - {0, 0x03, 169}, - {0, 0x03, 170}, - {0, 0x03, 173}, - }, - /* 120 */ - { - {2, 0x02, 230}, - {9, 0x02, 230}, - {23, 0x02, 230}, - {40, 0x03, 230}, - {1, 0x02, 129}, - {22, 0x03, 129}, - {1, 0x02, 132}, - {22, 0x03, 132}, - {1, 0x02, 133}, - {22, 0x03, 133}, - {1, 0x02, 134}, - {22, 0x03, 134}, - {1, 0x02, 136}, - {22, 0x03, 136}, - {1, 0x02, 146}, - {22, 0x03, 146}, - }, - /* 121 */ - { - {3, 0x02, 230}, - {6, 0x02, 230}, - {10, 0x02, 230}, - {15, 0x02, 230}, - {24, 0x02, 230}, - {31, 0x02, 230}, - {41, 0x02, 230}, - {56, 0x03, 230}, - {2, 0x02, 129}, - {9, 0x02, 129}, - {23, 0x02, 129}, - {40, 0x03, 129}, - {2, 0x02, 132}, - {9, 0x02, 132}, - {23, 0x02, 132}, - {40, 0x03, 132}, - }, - /* 122 */ - { - {3, 0x02, 129}, - {6, 0x02, 129}, - {10, 0x02, 129}, - {15, 0x02, 129}, - {24, 0x02, 129}, - {31, 0x02, 129}, - {41, 0x02, 129}, - {56, 0x03, 129}, - {3, 0x02, 132}, - {6, 0x02, 132}, - {10, 0x02, 132}, - {15, 0x02, 132}, - {24, 0x02, 132}, - {31, 0x02, 132}, - {41, 0x02, 132}, - {56, 0x03, 132}, - }, - /* 123 */ - { - {2, 0x02, 133}, - {9, 0x02, 133}, - {23, 0x02, 133}, - {40, 0x03, 133}, - {2, 0x02, 134}, - {9, 0x02, 134}, - {23, 0x02, 134}, - {40, 0x03, 134}, - {2, 0x02, 136}, - {9, 0x02, 136}, - {23, 0x02, 136}, - {40, 0x03, 136}, - {2, 0x02, 146}, - {9, 0x02, 146}, - {23, 0x02, 146}, - {40, 0x03, 146}, - }, - /* 124 */ - { - {3, 0x02, 133}, - {6, 0x02, 133}, - {10, 0x02, 133}, - {15, 0x02, 133}, - {24, 0x02, 133}, - {31, 0x02, 133}, - {41, 0x02, 133}, - {56, 0x03, 133}, - {3, 0x02, 134}, - {6, 0x02, 134}, - {10, 0x02, 134}, - {15, 0x02, 134}, - {24, 0x02, 134}, - {31, 0x02, 134}, - {41, 0x02, 134}, - {56, 0x03, 134}, - }, - /* 125 */ - { - {3, 0x02, 136}, - {6, 0x02, 136}, - {10, 0x02, 136}, - {15, 0x02, 136}, - {24, 0x02, 136}, - {31, 0x02, 136}, - {41, 0x02, 136}, - {56, 0x03, 136}, - {3, 0x02, 146}, - {6, 0x02, 146}, - {10, 0x02, 146}, - {15, 0x02, 146}, - {24, 0x02, 146}, - {31, 0x02, 146}, - {41, 0x02, 146}, - {56, 0x03, 146}, - }, - /* 126 */ - { - {1, 0x02, 154}, - {22, 0x03, 154}, - {1, 0x02, 156}, - {22, 0x03, 156}, - {1, 0x02, 160}, - {22, 0x03, 160}, - {1, 0x02, 163}, - {22, 0x03, 163}, - {1, 0x02, 164}, - {22, 0x03, 164}, - {1, 0x02, 169}, - {22, 0x03, 169}, - {1, 0x02, 170}, - {22, 0x03, 170}, - {1, 0x02, 173}, - {22, 0x03, 173}, - }, - /* 127 */ - { - {2, 0x02, 154}, - {9, 0x02, 154}, - {23, 0x02, 154}, - {40, 0x03, 154}, - {2, 0x02, 156}, - {9, 0x02, 156}, - {23, 0x02, 156}, - {40, 0x03, 156}, - {2, 0x02, 160}, - {9, 0x02, 160}, - {23, 0x02, 160}, - {40, 0x03, 160}, - {2, 0x02, 163}, - {9, 0x02, 163}, - {23, 0x02, 163}, - {40, 0x03, 163}, - }, - /* 128 */ - { - {3, 0x02, 154}, - {6, 0x02, 154}, - {10, 0x02, 154}, - {15, 0x02, 154}, - {24, 0x02, 154}, - {31, 0x02, 154}, - {41, 0x02, 154}, - {56, 0x03, 154}, - {3, 0x02, 156}, - {6, 0x02, 156}, - {10, 0x02, 156}, - {15, 0x02, 156}, - {24, 0x02, 156}, - {31, 0x02, 156}, - {41, 0x02, 156}, - {56, 0x03, 156}, - }, - /* 129 */ - { - {3, 0x02, 160}, - {6, 0x02, 160}, - {10, 0x02, 160}, - {15, 0x02, 160}, - {24, 0x02, 160}, - {31, 0x02, 160}, - {41, 0x02, 160}, - {56, 0x03, 160}, - {3, 0x02, 163}, - {6, 0x02, 163}, - {10, 0x02, 163}, - {15, 0x02, 163}, - {24, 0x02, 163}, - {31, 0x02, 163}, - {41, 0x02, 163}, - {56, 0x03, 163}, - }, - /* 130 */ - { - {2, 0x02, 164}, - {9, 0x02, 164}, - {23, 0x02, 164}, - {40, 0x03, 164}, - {2, 0x02, 169}, - {9, 0x02, 169}, - {23, 0x02, 169}, - {40, 0x03, 169}, - {2, 0x02, 170}, - {9, 0x02, 170}, - {23, 0x02, 170}, - {40, 0x03, 170}, - {2, 0x02, 173}, - {9, 0x02, 173}, - {23, 0x02, 173}, - {40, 0x03, 173}, - }, - /* 131 */ - { - {3, 0x02, 164}, - {6, 0x02, 164}, - {10, 0x02, 164}, - {15, 0x02, 164}, - {24, 0x02, 164}, - {31, 0x02, 164}, - {41, 0x02, 164}, - {56, 0x03, 164}, - {3, 0x02, 169}, - {6, 0x02, 169}, - {10, 0x02, 169}, - {15, 0x02, 169}, - {24, 0x02, 169}, - {31, 0x02, 169}, - {41, 0x02, 169}, - {56, 0x03, 169}, - }, - /* 132 */ - { - {3, 0x02, 170}, - {6, 0x02, 170}, - {10, 0x02, 170}, - {15, 0x02, 170}, - {24, 0x02, 170}, - {31, 0x02, 170}, - {41, 0x02, 170}, - {56, 0x03, 170}, - {3, 0x02, 173}, - {6, 0x02, 173}, - {10, 0x02, 173}, - {15, 0x02, 173}, - {24, 0x02, 173}, - {31, 0x02, 173}, - {41, 0x02, 173}, - {56, 0x03, 173}, - }, - /* 133 */ - { - {137, 0x00, 0}, - {138, 0x00, 0}, - {140, 0x00, 0}, - {141, 0x00, 0}, - {144, 0x00, 0}, - {145, 0x00, 0}, - {147, 0x00, 0}, - {150, 0x00, 0}, - {156, 0x00, 0}, - {159, 0x00, 0}, - {163, 0x00, 0}, - {166, 0x00, 0}, - {171, 0x00, 0}, - {174, 0x00, 0}, - {181, 0x00, 0}, - {190, 0x00, 0}, - }, - /* 134 */ - { - {0, 0x03, 178}, - {0, 0x03, 181}, - {0, 0x03, 185}, - {0, 0x03, 186}, - {0, 0x03, 187}, - {0, 0x03, 189}, - {0, 0x03, 190}, - {0, 0x03, 196}, - {0, 0x03, 198}, - {0, 0x03, 228}, - {0, 0x03, 232}, - {0, 0x03, 233}, - {148, 0x00, 0}, - {149, 0x00, 0}, - {151, 0x00, 0}, - {152, 0x00, 0}, - }, - /* 135 */ - { - {1, 0x02, 178}, - {22, 0x03, 178}, - {1, 0x02, 181}, - {22, 0x03, 181}, - {1, 0x02, 185}, - {22, 0x03, 185}, - {1, 0x02, 186}, - {22, 0x03, 186}, - {1, 0x02, 187}, - {22, 0x03, 187}, - {1, 0x02, 189}, - {22, 0x03, 189}, - {1, 0x02, 190}, - {22, 0x03, 190}, - {1, 0x02, 196}, - {22, 0x03, 196}, - }, - /* 136 */ - { - {2, 0x02, 178}, - {9, 0x02, 178}, - {23, 0x02, 178}, - {40, 0x03, 178}, - {2, 0x02, 181}, - {9, 0x02, 181}, - {23, 0x02, 181}, - {40, 0x03, 181}, - {2, 0x02, 185}, - {9, 0x02, 185}, - {23, 0x02, 185}, - {40, 0x03, 185}, - {2, 0x02, 186}, - {9, 0x02, 186}, - {23, 0x02, 186}, - {40, 0x03, 186}, - }, - /* 137 */ - { - {3, 0x02, 178}, - {6, 0x02, 178}, - {10, 0x02, 178}, - {15, 0x02, 178}, - {24, 0x02, 178}, - {31, 0x02, 178}, - {41, 0x02, 178}, - {56, 0x03, 178}, - {3, 0x02, 181}, - {6, 0x02, 181}, - {10, 0x02, 181}, - {15, 0x02, 181}, - {24, 0x02, 181}, - {31, 0x02, 181}, - {41, 0x02, 181}, - {56, 0x03, 181}, - }, - /* 138 */ - { - {3, 0x02, 185}, - {6, 0x02, 185}, - {10, 0x02, 185}, - {15, 0x02, 185}, - {24, 0x02, 185}, - {31, 0x02, 185}, - {41, 0x02, 185}, - {56, 0x03, 185}, - {3, 0x02, 186}, - {6, 0x02, 186}, - {10, 0x02, 186}, - {15, 0x02, 186}, - {24, 0x02, 186}, - {31, 0x02, 186}, - {41, 0x02, 186}, - {56, 0x03, 186}, - }, - /* 139 */ - { - {2, 0x02, 187}, - {9, 0x02, 187}, - {23, 0x02, 187}, - {40, 0x03, 187}, - {2, 0x02, 189}, - {9, 0x02, 189}, - {23, 0x02, 189}, - {40, 0x03, 189}, - {2, 0x02, 190}, - {9, 0x02, 190}, - {23, 0x02, 190}, - {40, 0x03, 190}, - {2, 0x02, 196}, - {9, 0x02, 196}, - {23, 0x02, 196}, - {40, 0x03, 196}, - }, - /* 140 */ - { - {3, 0x02, 187}, - {6, 0x02, 187}, - {10, 0x02, 187}, - {15, 0x02, 187}, - {24, 0x02, 187}, - {31, 0x02, 187}, - {41, 0x02, 187}, - {56, 0x03, 187}, - {3, 0x02, 189}, - {6, 0x02, 189}, - {10, 0x02, 189}, - {15, 0x02, 189}, - {24, 0x02, 189}, - {31, 0x02, 189}, - {41, 0x02, 189}, - {56, 0x03, 189}, - }, - /* 141 */ - { - {3, 0x02, 190}, - {6, 0x02, 190}, - {10, 0x02, 190}, - {15, 0x02, 190}, - {24, 0x02, 190}, - {31, 0x02, 190}, - {41, 0x02, 190}, - {56, 0x03, 190}, - {3, 0x02, 196}, - {6, 0x02, 196}, - {10, 0x02, 196}, - {15, 0x02, 196}, - {24, 0x02, 196}, - {31, 0x02, 196}, - {41, 0x02, 196}, - {56, 0x03, 196}, - }, - /* 142 */ - { - {1, 0x02, 198}, - {22, 0x03, 198}, - {1, 0x02, 228}, - {22, 0x03, 228}, - {1, 0x02, 232}, - {22, 0x03, 232}, - {1, 0x02, 233}, - {22, 0x03, 233}, - {0, 0x03, 1}, - {0, 0x03, 135}, - {0, 0x03, 137}, - {0, 0x03, 138}, - {0, 0x03, 139}, - {0, 0x03, 140}, - {0, 0x03, 141}, - {0, 0x03, 143}, - }, - /* 143 */ - { - {2, 0x02, 198}, - {9, 0x02, 198}, - {23, 0x02, 198}, - {40, 0x03, 198}, - {2, 0x02, 228}, - {9, 0x02, 228}, - {23, 0x02, 228}, - {40, 0x03, 228}, - {2, 0x02, 232}, - {9, 0x02, 232}, - {23, 0x02, 232}, - {40, 0x03, 232}, - {2, 0x02, 233}, - {9, 0x02, 233}, - {23, 0x02, 233}, - {40, 0x03, 233}, - }, - /* 144 */ - { - {3, 0x02, 198}, - {6, 0x02, 198}, - {10, 0x02, 198}, - {15, 0x02, 198}, - {24, 0x02, 198}, - {31, 0x02, 198}, - {41, 0x02, 198}, - {56, 0x03, 198}, - {3, 0x02, 228}, - {6, 0x02, 228}, - {10, 0x02, 228}, - {15, 0x02, 228}, - {24, 0x02, 228}, - {31, 0x02, 228}, - {41, 0x02, 228}, - {56, 0x03, 228}, - }, - /* 145 */ - { - {3, 0x02, 232}, - {6, 0x02, 232}, - {10, 0x02, 232}, - {15, 0x02, 232}, - {24, 0x02, 232}, - {31, 0x02, 232}, - {41, 0x02, 232}, - {56, 0x03, 232}, - {3, 0x02, 233}, - {6, 0x02, 233}, - {10, 0x02, 233}, - {15, 0x02, 233}, - {24, 0x02, 233}, - {31, 0x02, 233}, - {41, 0x02, 233}, - {56, 0x03, 233}, - }, - /* 146 */ - { - {1, 0x02, 1}, - {22, 0x03, 1}, - {1, 0x02, 135}, - {22, 0x03, 135}, - {1, 0x02, 137}, - {22, 0x03, 137}, - {1, 0x02, 138}, - {22, 0x03, 138}, - {1, 0x02, 139}, - {22, 0x03, 139}, - {1, 0x02, 140}, - {22, 0x03, 140}, - {1, 0x02, 141}, - {22, 0x03, 141}, - {1, 0x02, 143}, - {22, 0x03, 143}, - }, - /* 147 */ - { - {2, 0x02, 1}, - {9, 0x02, 1}, - {23, 0x02, 1}, - {40, 0x03, 1}, - {2, 0x02, 135}, - {9, 0x02, 135}, - {23, 0x02, 135}, - {40, 0x03, 135}, - {2, 0x02, 137}, - {9, 0x02, 137}, - {23, 0x02, 137}, - {40, 0x03, 137}, - {2, 0x02, 138}, - {9, 0x02, 138}, - {23, 0x02, 138}, - {40, 0x03, 138}, - }, - /* 148 */ - { - {3, 0x02, 1}, - {6, 0x02, 1}, - {10, 0x02, 1}, - {15, 0x02, 1}, - {24, 0x02, 1}, - {31, 0x02, 1}, - {41, 0x02, 1}, - {56, 0x03, 1}, - {3, 0x02, 135}, - {6, 0x02, 135}, - {10, 0x02, 135}, - {15, 0x02, 135}, - {24, 0x02, 135}, - {31, 0x02, 135}, - {41, 0x02, 135}, - {56, 0x03, 135}, - }, - /* 149 */ - { - {3, 0x02, 137}, - {6, 0x02, 137}, - {10, 0x02, 137}, - {15, 0x02, 137}, - {24, 0x02, 137}, - {31, 0x02, 137}, - {41, 0x02, 137}, - {56, 0x03, 137}, - {3, 0x02, 138}, - {6, 0x02, 138}, - {10, 0x02, 138}, - {15, 0x02, 138}, - {24, 0x02, 138}, - {31, 0x02, 138}, - {41, 0x02, 138}, - {56, 0x03, 138}, - }, - /* 150 */ - { - {2, 0x02, 139}, - {9, 0x02, 139}, - {23, 0x02, 139}, - {40, 0x03, 139}, - {2, 0x02, 140}, - {9, 0x02, 140}, - {23, 0x02, 140}, - {40, 0x03, 140}, - {2, 0x02, 141}, - {9, 0x02, 141}, - {23, 0x02, 141}, - {40, 0x03, 141}, - {2, 0x02, 143}, - {9, 0x02, 143}, - {23, 0x02, 143}, - {40, 0x03, 143}, - }, - /* 151 */ - { - {3, 0x02, 139}, - {6, 0x02, 139}, - {10, 0x02, 139}, - {15, 0x02, 139}, - {24, 0x02, 139}, - {31, 0x02, 139}, - {41, 0x02, 139}, - {56, 0x03, 139}, - {3, 0x02, 140}, - {6, 0x02, 140}, - {10, 0x02, 140}, - {15, 0x02, 140}, - {24, 0x02, 140}, - {31, 0x02, 140}, - {41, 0x02, 140}, - {56, 0x03, 140}, - }, - /* 152 */ - { - {3, 0x02, 141}, - {6, 0x02, 141}, - {10, 0x02, 141}, - {15, 0x02, 141}, - {24, 0x02, 141}, - {31, 0x02, 141}, - {41, 0x02, 141}, - {56, 0x03, 141}, - {3, 0x02, 143}, - {6, 0x02, 143}, - {10, 0x02, 143}, - {15, 0x02, 143}, - {24, 0x02, 143}, - {31, 0x02, 143}, - {41, 0x02, 143}, - {56, 0x03, 143}, - }, - /* 153 */ - { - {157, 0x00, 0}, - {158, 0x00, 0}, - {160, 0x00, 0}, - {161, 0x00, 0}, - {164, 0x00, 0}, - {165, 0x00, 0}, - {167, 0x00, 0}, - {168, 0x00, 0}, - {172, 0x00, 0}, - {173, 0x00, 0}, - {175, 0x00, 0}, - {177, 0x00, 0}, - {182, 0x00, 0}, - {185, 0x00, 0}, - {191, 0x00, 0}, - {207, 0x00, 0}, - }, - /* 154 */ - { - {0, 0x03, 147}, - {0, 0x03, 149}, - {0, 0x03, 150}, - {0, 0x03, 151}, - {0, 0x03, 152}, - {0, 0x03, 155}, - {0, 0x03, 157}, - {0, 0x03, 158}, - {0, 0x03, 165}, - {0, 0x03, 166}, - {0, 0x03, 168}, - {0, 0x03, 174}, - {0, 0x03, 175}, - {0, 0x03, 180}, - {0, 0x03, 182}, - {0, 0x03, 183}, - }, - /* 155 */ - { - {1, 0x02, 147}, - {22, 0x03, 147}, - {1, 0x02, 149}, - {22, 0x03, 149}, - {1, 0x02, 150}, - {22, 0x03, 150}, - {1, 0x02, 151}, - {22, 0x03, 151}, - {1, 0x02, 152}, - {22, 0x03, 152}, - {1, 0x02, 155}, - {22, 0x03, 155}, - {1, 0x02, 157}, - {22, 0x03, 157}, - {1, 0x02, 158}, - {22, 0x03, 158}, - }, - /* 156 */ - { - {2, 0x02, 147}, - {9, 0x02, 147}, - {23, 0x02, 147}, - {40, 0x03, 147}, - {2, 0x02, 149}, - {9, 0x02, 149}, - {23, 0x02, 149}, - {40, 0x03, 149}, - {2, 0x02, 150}, - {9, 0x02, 150}, - {23, 0x02, 150}, - {40, 0x03, 150}, - {2, 0x02, 151}, - {9, 0x02, 151}, - {23, 0x02, 151}, - {40, 0x03, 151}, - }, - /* 157 */ - { - {3, 0x02, 147}, - {6, 0x02, 147}, - {10, 0x02, 147}, - {15, 0x02, 147}, - {24, 0x02, 147}, - {31, 0x02, 147}, - {41, 0x02, 147}, - {56, 0x03, 147}, - {3, 0x02, 149}, - {6, 0x02, 149}, - {10, 0x02, 149}, - {15, 0x02, 149}, - {24, 0x02, 149}, - {31, 0x02, 149}, - {41, 0x02, 149}, - {56, 0x03, 149}, - }, - /* 158 */ - { - {3, 0x02, 150}, - {6, 0x02, 150}, - {10, 0x02, 150}, - {15, 0x02, 150}, - {24, 0x02, 150}, - {31, 0x02, 150}, - {41, 0x02, 150}, - {56, 0x03, 150}, - {3, 0x02, 151}, - {6, 0x02, 151}, - {10, 0x02, 151}, - {15, 0x02, 151}, - {24, 0x02, 151}, - {31, 0x02, 151}, - {41, 0x02, 151}, - {56, 0x03, 151}, - }, - /* 159 */ - { - {2, 0x02, 152}, - {9, 0x02, 152}, - {23, 0x02, 152}, - {40, 0x03, 152}, - {2, 0x02, 155}, - {9, 0x02, 155}, - {23, 0x02, 155}, - {40, 0x03, 155}, - {2, 0x02, 157}, - {9, 0x02, 157}, - {23, 0x02, 157}, - {40, 0x03, 157}, - {2, 0x02, 158}, - {9, 0x02, 158}, - {23, 0x02, 158}, - {40, 0x03, 158}, - }, - /* 160 */ - { - {3, 0x02, 152}, - {6, 0x02, 152}, - {10, 0x02, 152}, - {15, 0x02, 152}, - {24, 0x02, 152}, - {31, 0x02, 152}, - {41, 0x02, 152}, - {56, 0x03, 152}, - {3, 0x02, 155}, - {6, 0x02, 155}, - {10, 0x02, 155}, - {15, 0x02, 155}, - {24, 0x02, 155}, - {31, 0x02, 155}, - {41, 0x02, 155}, - {56, 0x03, 155}, - }, - /* 161 */ - { - {3, 0x02, 157}, - {6, 0x02, 157}, - {10, 0x02, 157}, - {15, 0x02, 157}, - {24, 0x02, 157}, - {31, 0x02, 157}, - {41, 0x02, 157}, - {56, 0x03, 157}, - {3, 0x02, 158}, - {6, 0x02, 158}, - {10, 0x02, 158}, - {15, 0x02, 158}, - {24, 0x02, 158}, - {31, 0x02, 158}, - {41, 0x02, 158}, - {56, 0x03, 158}, - }, - /* 162 */ - { - {1, 0x02, 165}, - {22, 0x03, 165}, - {1, 0x02, 166}, - {22, 0x03, 166}, - {1, 0x02, 168}, - {22, 0x03, 168}, - {1, 0x02, 174}, - {22, 0x03, 174}, - {1, 0x02, 175}, - {22, 0x03, 175}, - {1, 0x02, 180}, - {22, 0x03, 180}, - {1, 0x02, 182}, - {22, 0x03, 182}, - {1, 0x02, 183}, - {22, 0x03, 183}, - }, - /* 163 */ - { - {2, 0x02, 165}, - {9, 0x02, 165}, - {23, 0x02, 165}, - {40, 0x03, 165}, - {2, 0x02, 166}, - {9, 0x02, 166}, - {23, 0x02, 166}, - {40, 0x03, 166}, - {2, 0x02, 168}, - {9, 0x02, 168}, - {23, 0x02, 168}, - {40, 0x03, 168}, - {2, 0x02, 174}, - {9, 0x02, 174}, - {23, 0x02, 174}, - {40, 0x03, 174}, - }, - /* 164 */ - { - {3, 0x02, 165}, - {6, 0x02, 165}, - {10, 0x02, 165}, - {15, 0x02, 165}, - {24, 0x02, 165}, - {31, 0x02, 165}, - {41, 0x02, 165}, - {56, 0x03, 165}, - {3, 0x02, 166}, - {6, 0x02, 166}, - {10, 0x02, 166}, - {15, 0x02, 166}, - {24, 0x02, 166}, - {31, 0x02, 166}, - {41, 0x02, 166}, - {56, 0x03, 166}, - }, - /* 165 */ - { - {3, 0x02, 168}, - {6, 0x02, 168}, - {10, 0x02, 168}, - {15, 0x02, 168}, - {24, 0x02, 168}, - {31, 0x02, 168}, - {41, 0x02, 168}, - {56, 0x03, 168}, - {3, 0x02, 174}, - {6, 0x02, 174}, - {10, 0x02, 174}, - {15, 0x02, 174}, - {24, 0x02, 174}, - {31, 0x02, 174}, - {41, 0x02, 174}, - {56, 0x03, 174}, - }, - /* 166 */ - { - {2, 0x02, 175}, - {9, 0x02, 175}, - {23, 0x02, 175}, - {40, 0x03, 175}, - {2, 0x02, 180}, - {9, 0x02, 180}, - {23, 0x02, 180}, - {40, 0x03, 180}, - {2, 0x02, 182}, - {9, 0x02, 182}, - {23, 0x02, 182}, - {40, 0x03, 182}, - {2, 0x02, 183}, - {9, 0x02, 183}, - {23, 0x02, 183}, - {40, 0x03, 183}, - }, - /* 167 */ - { - {3, 0x02, 175}, - {6, 0x02, 175}, - {10, 0x02, 175}, - {15, 0x02, 175}, - {24, 0x02, 175}, - {31, 0x02, 175}, - {41, 0x02, 175}, - {56, 0x03, 175}, - {3, 0x02, 180}, - {6, 0x02, 180}, - {10, 0x02, 180}, - {15, 0x02, 180}, - {24, 0x02, 180}, - {31, 0x02, 180}, - {41, 0x02, 180}, - {56, 0x03, 180}, - }, - /* 168 */ - { - {3, 0x02, 182}, - {6, 0x02, 182}, - {10, 0x02, 182}, - {15, 0x02, 182}, - {24, 0x02, 182}, - {31, 0x02, 182}, - {41, 0x02, 182}, - {56, 0x03, 182}, - {3, 0x02, 183}, - {6, 0x02, 183}, - {10, 0x02, 183}, - {15, 0x02, 183}, - {24, 0x02, 183}, - {31, 0x02, 183}, - {41, 0x02, 183}, - {56, 0x03, 183}, - }, - /* 169 */ - { - {0, 0x03, 188}, - {0, 0x03, 191}, - {0, 0x03, 197}, - {0, 0x03, 231}, - {0, 0x03, 239}, - {176, 0x00, 0}, - {178, 0x00, 0}, - {179, 0x00, 0}, - {183, 0x00, 0}, - {184, 0x00, 0}, - {186, 0x00, 0}, - {187, 0x00, 0}, - {192, 0x00, 0}, - {199, 0x00, 0}, - {208, 0x00, 0}, - {223, 0x00, 0}, - }, - /* 170 */ - { - {1, 0x02, 188}, - {22, 0x03, 188}, - {1, 0x02, 191}, - {22, 0x03, 191}, - {1, 0x02, 197}, - {22, 0x03, 197}, - {1, 0x02, 231}, - {22, 0x03, 231}, - {1, 0x02, 239}, - {22, 0x03, 239}, - {0, 0x03, 9}, - {0, 0x03, 142}, - {0, 0x03, 144}, - {0, 0x03, 145}, - {0, 0x03, 148}, - {0, 0x03, 159}, - }, - /* 171 */ - { - {2, 0x02, 188}, - {9, 0x02, 188}, - {23, 0x02, 188}, - {40, 0x03, 188}, - {2, 0x02, 191}, - {9, 0x02, 191}, - {23, 0x02, 191}, - {40, 0x03, 191}, - {2, 0x02, 197}, - {9, 0x02, 197}, - {23, 0x02, 197}, - {40, 0x03, 197}, - {2, 0x02, 231}, - {9, 0x02, 231}, - {23, 0x02, 231}, - {40, 0x03, 231}, - }, - /* 172 */ - { - {3, 0x02, 188}, - {6, 0x02, 188}, - {10, 0x02, 188}, - {15, 0x02, 188}, - {24, 0x02, 188}, - {31, 0x02, 188}, - {41, 0x02, 188}, - {56, 0x03, 188}, - {3, 0x02, 191}, - {6, 0x02, 191}, - {10, 0x02, 191}, - {15, 0x02, 191}, - {24, 0x02, 191}, - {31, 0x02, 191}, - {41, 0x02, 191}, - {56, 0x03, 191}, - }, - /* 173 */ - { - {3, 0x02, 197}, - {6, 0x02, 197}, - {10, 0x02, 197}, - {15, 0x02, 197}, - {24, 0x02, 197}, - {31, 0x02, 197}, - {41, 0x02, 197}, - {56, 0x03, 197}, - {3, 0x02, 231}, - {6, 0x02, 231}, - {10, 0x02, 231}, - {15, 0x02, 231}, - {24, 0x02, 231}, - {31, 0x02, 231}, - {41, 0x02, 231}, - {56, 0x03, 231}, - }, - /* 174 */ - { - {2, 0x02, 239}, - {9, 0x02, 239}, - {23, 0x02, 239}, - {40, 0x03, 239}, - {1, 0x02, 9}, - {22, 0x03, 9}, - {1, 0x02, 142}, - {22, 0x03, 142}, - {1, 0x02, 144}, - {22, 0x03, 144}, - {1, 0x02, 145}, - {22, 0x03, 145}, - {1, 0x02, 148}, - {22, 0x03, 148}, - {1, 0x02, 159}, - {22, 0x03, 159}, - }, - /* 175 */ - { - {3, 0x02, 239}, - {6, 0x02, 239}, - {10, 0x02, 239}, - {15, 0x02, 239}, - {24, 0x02, 239}, - {31, 0x02, 239}, - {41, 0x02, 239}, - {56, 0x03, 239}, - {2, 0x02, 9}, - {9, 0x02, 9}, - {23, 0x02, 9}, - {40, 0x03, 9}, - {2, 0x02, 142}, - {9, 0x02, 142}, - {23, 0x02, 142}, - {40, 0x03, 142}, - }, - /* 176 */ - { - {3, 0x02, 9}, - {6, 0x02, 9}, - {10, 0x02, 9}, - {15, 0x02, 9}, - {24, 0x02, 9}, - {31, 0x02, 9}, - {41, 0x02, 9}, - {56, 0x03, 9}, - {3, 0x02, 142}, - {6, 0x02, 142}, - {10, 0x02, 142}, - {15, 0x02, 142}, - {24, 0x02, 142}, - {31, 0x02, 142}, - {41, 0x02, 142}, - {56, 0x03, 142}, - }, - /* 177 */ - { - {2, 0x02, 144}, - {9, 0x02, 144}, - {23, 0x02, 144}, - {40, 0x03, 144}, - {2, 0x02, 145}, - {9, 0x02, 145}, - {23, 0x02, 145}, - {40, 0x03, 145}, - {2, 0x02, 148}, - {9, 0x02, 148}, - {23, 0x02, 148}, - {40, 0x03, 148}, - {2, 0x02, 159}, - {9, 0x02, 159}, - {23, 0x02, 159}, - {40, 0x03, 159}, - }, - /* 178 */ - { - {3, 0x02, 144}, - {6, 0x02, 144}, - {10, 0x02, 144}, - {15, 0x02, 144}, - {24, 0x02, 144}, - {31, 0x02, 144}, - {41, 0x02, 144}, - {56, 0x03, 144}, - {3, 0x02, 145}, - {6, 0x02, 145}, - {10, 0x02, 145}, - {15, 0x02, 145}, - {24, 0x02, 145}, - {31, 0x02, 145}, - {41, 0x02, 145}, - {56, 0x03, 145}, - }, - /* 179 */ - { - {3, 0x02, 148}, - {6, 0x02, 148}, - {10, 0x02, 148}, - {15, 0x02, 148}, - {24, 0x02, 148}, - {31, 0x02, 148}, - {41, 0x02, 148}, - {56, 0x03, 148}, - {3, 0x02, 159}, - {6, 0x02, 159}, - {10, 0x02, 159}, - {15, 0x02, 159}, - {24, 0x02, 159}, - {31, 0x02, 159}, - {41, 0x02, 159}, - {56, 0x03, 159}, - }, - /* 180 */ - { - {0, 0x03, 171}, - {0, 0x03, 206}, - {0, 0x03, 215}, - {0, 0x03, 225}, - {0, 0x03, 236}, - {0, 0x03, 237}, - {188, 0x00, 0}, - {189, 0x00, 0}, - {193, 0x00, 0}, - {196, 0x00, 0}, - {200, 0x00, 0}, - {203, 0x00, 0}, - {209, 0x00, 0}, - {216, 0x00, 0}, - {224, 0x00, 0}, - {238, 0x00, 0}, - }, - /* 181 */ - { - {1, 0x02, 171}, - {22, 0x03, 171}, - {1, 0x02, 206}, - {22, 0x03, 206}, - {1, 0x02, 215}, - {22, 0x03, 215}, - {1, 0x02, 225}, - {22, 0x03, 225}, - {1, 0x02, 236}, - {22, 0x03, 236}, - {1, 0x02, 237}, - {22, 0x03, 237}, - {0, 0x03, 199}, - {0, 0x03, 207}, - {0, 0x03, 234}, - {0, 0x03, 235}, - }, - /* 182 */ - { - {2, 0x02, 171}, - {9, 0x02, 171}, - {23, 0x02, 171}, - {40, 0x03, 171}, - {2, 0x02, 206}, - {9, 0x02, 206}, - {23, 0x02, 206}, - {40, 0x03, 206}, - {2, 0x02, 215}, - {9, 0x02, 215}, - {23, 0x02, 215}, - {40, 0x03, 215}, - {2, 0x02, 225}, - {9, 0x02, 225}, - {23, 0x02, 225}, - {40, 0x03, 225}, - }, - /* 183 */ - { - {3, 0x02, 171}, - {6, 0x02, 171}, - {10, 0x02, 171}, - {15, 0x02, 171}, - {24, 0x02, 171}, - {31, 0x02, 171}, - {41, 0x02, 171}, - {56, 0x03, 171}, - {3, 0x02, 206}, - {6, 0x02, 206}, - {10, 0x02, 206}, - {15, 0x02, 206}, - {24, 0x02, 206}, - {31, 0x02, 206}, - {41, 0x02, 206}, - {56, 0x03, 206}, - }, - /* 184 */ - { - {3, 0x02, 215}, - {6, 0x02, 215}, - {10, 0x02, 215}, - {15, 0x02, 215}, - {24, 0x02, 215}, - {31, 0x02, 215}, - {41, 0x02, 215}, - {56, 0x03, 215}, - {3, 0x02, 225}, - {6, 0x02, 225}, - {10, 0x02, 225}, - {15, 0x02, 225}, - {24, 0x02, 225}, - {31, 0x02, 225}, - {41, 0x02, 225}, - {56, 0x03, 225}, - }, - /* 185 */ - { - {2, 0x02, 236}, - {9, 0x02, 236}, - {23, 0x02, 236}, - {40, 0x03, 236}, - {2, 0x02, 237}, - {9, 0x02, 237}, - {23, 0x02, 237}, - {40, 0x03, 237}, - {1, 0x02, 199}, - {22, 0x03, 199}, - {1, 0x02, 207}, - {22, 0x03, 207}, - {1, 0x02, 234}, - {22, 0x03, 234}, - {1, 0x02, 235}, - {22, 0x03, 235}, - }, - /* 186 */ - { - {3, 0x02, 236}, - {6, 0x02, 236}, - {10, 0x02, 236}, - {15, 0x02, 236}, - {24, 0x02, 236}, - {31, 0x02, 236}, - {41, 0x02, 236}, - {56, 0x03, 236}, - {3, 0x02, 237}, - {6, 0x02, 237}, - {10, 0x02, 237}, - {15, 0x02, 237}, - {24, 0x02, 237}, - {31, 0x02, 237}, - {41, 0x02, 237}, - {56, 0x03, 237}, - }, - /* 187 */ - { - {2, 0x02, 199}, - {9, 0x02, 199}, - {23, 0x02, 199}, - {40, 0x03, 199}, - {2, 0x02, 207}, - {9, 0x02, 207}, - {23, 0x02, 207}, - {40, 0x03, 207}, - {2, 0x02, 234}, - {9, 0x02, 234}, - {23, 0x02, 234}, - {40, 0x03, 234}, - {2, 0x02, 235}, - {9, 0x02, 235}, - {23, 0x02, 235}, - {40, 0x03, 235}, - }, - /* 188 */ - { - {3, 0x02, 199}, - {6, 0x02, 199}, - {10, 0x02, 199}, - {15, 0x02, 199}, - {24, 0x02, 199}, - {31, 0x02, 199}, - {41, 0x02, 199}, - {56, 0x03, 199}, - {3, 0x02, 207}, - {6, 0x02, 207}, - {10, 0x02, 207}, - {15, 0x02, 207}, - {24, 0x02, 207}, - {31, 0x02, 207}, - {41, 0x02, 207}, - {56, 0x03, 207}, - }, - /* 189 */ - { - {3, 0x02, 234}, - {6, 0x02, 234}, - {10, 0x02, 234}, - {15, 0x02, 234}, - {24, 0x02, 234}, - {31, 0x02, 234}, - {41, 0x02, 234}, - {56, 0x03, 234}, - {3, 0x02, 235}, - {6, 0x02, 235}, - {10, 0x02, 235}, - {15, 0x02, 235}, - {24, 0x02, 235}, - {31, 0x02, 235}, - {41, 0x02, 235}, - {56, 0x03, 235}, - }, - /* 190 */ - { - {194, 0x00, 0}, - {195, 0x00, 0}, - {197, 0x00, 0}, - {198, 0x00, 0}, - {201, 0x00, 0}, - {202, 0x00, 0}, - {204, 0x00, 0}, - {205, 0x00, 0}, - {210, 0x00, 0}, - {213, 0x00, 0}, - {217, 0x00, 0}, - {220, 0x00, 0}, - {225, 0x00, 0}, - {231, 0x00, 0}, - {239, 0x00, 0}, - {246, 0x00, 0}, - }, - /* 191 */ - { - {0, 0x03, 192}, - {0, 0x03, 193}, - {0, 0x03, 200}, - {0, 0x03, 201}, - {0, 0x03, 202}, - {0, 0x03, 205}, - {0, 0x03, 210}, - {0, 0x03, 213}, - {0, 0x03, 218}, - {0, 0x03, 219}, - {0, 0x03, 238}, - {0, 0x03, 240}, - {0, 0x03, 242}, - {0, 0x03, 243}, - {0, 0x03, 255}, - {206, 0x00, 0}, - }, - /* 192 */ - { - {1, 0x02, 192}, - {22, 0x03, 192}, - {1, 0x02, 193}, - {22, 0x03, 193}, - {1, 0x02, 200}, - {22, 0x03, 200}, - {1, 0x02, 201}, - {22, 0x03, 201}, - {1, 0x02, 202}, - {22, 0x03, 202}, - {1, 0x02, 205}, - {22, 0x03, 205}, - {1, 0x02, 210}, - {22, 0x03, 210}, - {1, 0x02, 213}, - {22, 0x03, 213}, - }, - /* 193 */ - { - {2, 0x02, 192}, - {9, 0x02, 192}, - {23, 0x02, 192}, - {40, 0x03, 192}, - {2, 0x02, 193}, - {9, 0x02, 193}, - {23, 0x02, 193}, - {40, 0x03, 193}, - {2, 0x02, 200}, - {9, 0x02, 200}, - {23, 0x02, 200}, - {40, 0x03, 200}, - {2, 0x02, 201}, - {9, 0x02, 201}, - {23, 0x02, 201}, - {40, 0x03, 201}, - }, - /* 194 */ - { - {3, 0x02, 192}, - {6, 0x02, 192}, - {10, 0x02, 192}, - {15, 0x02, 192}, - {24, 0x02, 192}, - {31, 0x02, 192}, - {41, 0x02, 192}, - {56, 0x03, 192}, - {3, 0x02, 193}, - {6, 0x02, 193}, - {10, 0x02, 193}, - {15, 0x02, 193}, - {24, 0x02, 193}, - {31, 0x02, 193}, - {41, 0x02, 193}, - {56, 0x03, 193}, - }, - /* 195 */ - { - {3, 0x02, 200}, - {6, 0x02, 200}, - {10, 0x02, 200}, - {15, 0x02, 200}, - {24, 0x02, 200}, - {31, 0x02, 200}, - {41, 0x02, 200}, - {56, 0x03, 200}, - {3, 0x02, 201}, - {6, 0x02, 201}, - {10, 0x02, 201}, - {15, 0x02, 201}, - {24, 0x02, 201}, - {31, 0x02, 201}, - {41, 0x02, 201}, - {56, 0x03, 201}, - }, - /* 196 */ - { - {2, 0x02, 202}, - {9, 0x02, 202}, - {23, 0x02, 202}, - {40, 0x03, 202}, - {2, 0x02, 205}, - {9, 0x02, 205}, - {23, 0x02, 205}, - {40, 0x03, 205}, - {2, 0x02, 210}, - {9, 0x02, 210}, - {23, 0x02, 210}, - {40, 0x03, 210}, - {2, 0x02, 213}, - {9, 0x02, 213}, - {23, 0x02, 213}, - {40, 0x03, 213}, - }, - /* 197 */ - { - {3, 0x02, 202}, - {6, 0x02, 202}, - {10, 0x02, 202}, - {15, 0x02, 202}, - {24, 0x02, 202}, - {31, 0x02, 202}, - {41, 0x02, 202}, - {56, 0x03, 202}, - {3, 0x02, 205}, - {6, 0x02, 205}, - {10, 0x02, 205}, - {15, 0x02, 205}, - {24, 0x02, 205}, - {31, 0x02, 205}, - {41, 0x02, 205}, - {56, 0x03, 205}, - }, - /* 198 */ - { - {3, 0x02, 210}, - {6, 0x02, 210}, - {10, 0x02, 210}, - {15, 0x02, 210}, - {24, 0x02, 210}, - {31, 0x02, 210}, - {41, 0x02, 210}, - {56, 0x03, 210}, - {3, 0x02, 213}, - {6, 0x02, 213}, - {10, 0x02, 213}, - {15, 0x02, 213}, - {24, 0x02, 213}, - {31, 0x02, 213}, - {41, 0x02, 213}, - {56, 0x03, 213}, - }, - /* 199 */ - { - {1, 0x02, 218}, - {22, 0x03, 218}, - {1, 0x02, 219}, - {22, 0x03, 219}, - {1, 0x02, 238}, - {22, 0x03, 238}, - {1, 0x02, 240}, - {22, 0x03, 240}, - {1, 0x02, 242}, - {22, 0x03, 242}, - {1, 0x02, 243}, - {22, 0x03, 243}, - {1, 0x02, 255}, - {22, 0x03, 255}, - {0, 0x03, 203}, - {0, 0x03, 204}, - }, - /* 200 */ - { - {2, 0x02, 218}, - {9, 0x02, 218}, - {23, 0x02, 218}, - {40, 0x03, 218}, - {2, 0x02, 219}, - {9, 0x02, 219}, - {23, 0x02, 219}, - {40, 0x03, 219}, - {2, 0x02, 238}, - {9, 0x02, 238}, - {23, 0x02, 238}, - {40, 0x03, 238}, - {2, 0x02, 240}, - {9, 0x02, 240}, - {23, 0x02, 240}, - {40, 0x03, 240}, - }, - /* 201 */ - { - {3, 0x02, 218}, - {6, 0x02, 218}, - {10, 0x02, 218}, - {15, 0x02, 218}, - {24, 0x02, 218}, - {31, 0x02, 218}, - {41, 0x02, 218}, - {56, 0x03, 218}, - {3, 0x02, 219}, - {6, 0x02, 219}, - {10, 0x02, 219}, - {15, 0x02, 219}, - {24, 0x02, 219}, - {31, 0x02, 219}, - {41, 0x02, 219}, - {56, 0x03, 219}, - }, - /* 202 */ - { - {3, 0x02, 238}, - {6, 0x02, 238}, - {10, 0x02, 238}, - {15, 0x02, 238}, - {24, 0x02, 238}, - {31, 0x02, 238}, - {41, 0x02, 238}, - {56, 0x03, 238}, - {3, 0x02, 240}, - {6, 0x02, 240}, - {10, 0x02, 240}, - {15, 0x02, 240}, - {24, 0x02, 240}, - {31, 0x02, 240}, - {41, 0x02, 240}, - {56, 0x03, 240}, - }, - /* 203 */ - { - {2, 0x02, 242}, - {9, 0x02, 242}, - {23, 0x02, 242}, - {40, 0x03, 242}, - {2, 0x02, 243}, - {9, 0x02, 243}, - {23, 0x02, 243}, - {40, 0x03, 243}, - {2, 0x02, 255}, - {9, 0x02, 255}, - {23, 0x02, 255}, - {40, 0x03, 255}, - {1, 0x02, 203}, - {22, 0x03, 203}, - {1, 0x02, 204}, - {22, 0x03, 204}, - }, - /* 204 */ - { - {3, 0x02, 242}, - {6, 0x02, 242}, - {10, 0x02, 242}, - {15, 0x02, 242}, - {24, 0x02, 242}, - {31, 0x02, 242}, - {41, 0x02, 242}, - {56, 0x03, 242}, - {3, 0x02, 243}, - {6, 0x02, 243}, - {10, 0x02, 243}, - {15, 0x02, 243}, - {24, 0x02, 243}, - {31, 0x02, 243}, - {41, 0x02, 243}, - {56, 0x03, 243}, - }, - /* 205 */ - { - {3, 0x02, 255}, - {6, 0x02, 255}, - {10, 0x02, 255}, - {15, 0x02, 255}, - {24, 0x02, 255}, - {31, 0x02, 255}, - {41, 0x02, 255}, - {56, 0x03, 255}, - {2, 0x02, 203}, - {9, 0x02, 203}, - {23, 0x02, 203}, - {40, 0x03, 203}, - {2, 0x02, 204}, - {9, 0x02, 204}, - {23, 0x02, 204}, - {40, 0x03, 204}, - }, - /* 206 */ - { - {3, 0x02, 203}, - {6, 0x02, 203}, - {10, 0x02, 203}, - {15, 0x02, 203}, - {24, 0x02, 203}, - {31, 0x02, 203}, - {41, 0x02, 203}, - {56, 0x03, 203}, - {3, 0x02, 204}, - {6, 0x02, 204}, - {10, 0x02, 204}, - {15, 0x02, 204}, - {24, 0x02, 204}, - {31, 0x02, 204}, - {41, 0x02, 204}, - {56, 0x03, 204}, - }, - /* 207 */ - { - {211, 0x00, 0}, - {212, 0x00, 0}, - {214, 0x00, 0}, - {215, 0x00, 0}, - {218, 0x00, 0}, - {219, 0x00, 0}, - {221, 0x00, 0}, - {222, 0x00, 0}, - {226, 0x00, 0}, - {228, 0x00, 0}, - {232, 0x00, 0}, - {235, 0x00, 0}, - {240, 0x00, 0}, - {243, 0x00, 0}, - {247, 0x00, 0}, - {250, 0x00, 0}, - }, - /* 208 */ - { - {0, 0x03, 211}, - {0, 0x03, 212}, - {0, 0x03, 214}, - {0, 0x03, 221}, - {0, 0x03, 222}, - {0, 0x03, 223}, - {0, 0x03, 241}, - {0, 0x03, 244}, - {0, 0x03, 245}, - {0, 0x03, 246}, - {0, 0x03, 247}, - {0, 0x03, 248}, - {0, 0x03, 250}, - {0, 0x03, 251}, - {0, 0x03, 252}, - {0, 0x03, 253}, - }, - /* 209 */ - { - {1, 0x02, 211}, - {22, 0x03, 211}, - {1, 0x02, 212}, - {22, 0x03, 212}, - {1, 0x02, 214}, - {22, 0x03, 214}, - {1, 0x02, 221}, - {22, 0x03, 221}, - {1, 0x02, 222}, - {22, 0x03, 222}, - {1, 0x02, 223}, - {22, 0x03, 223}, - {1, 0x02, 241}, - {22, 0x03, 241}, - {1, 0x02, 244}, - {22, 0x03, 244}, - }, - /* 210 */ - { - {2, 0x02, 211}, - {9, 0x02, 211}, - {23, 0x02, 211}, - {40, 0x03, 211}, - {2, 0x02, 212}, - {9, 0x02, 212}, - {23, 0x02, 212}, - {40, 0x03, 212}, - {2, 0x02, 214}, - {9, 0x02, 214}, - {23, 0x02, 214}, - {40, 0x03, 214}, - {2, 0x02, 221}, - {9, 0x02, 221}, - {23, 0x02, 221}, - {40, 0x03, 221}, - }, - /* 211 */ - { - {3, 0x02, 211}, - {6, 0x02, 211}, - {10, 0x02, 211}, - {15, 0x02, 211}, - {24, 0x02, 211}, - {31, 0x02, 211}, - {41, 0x02, 211}, - {56, 0x03, 211}, - {3, 0x02, 212}, - {6, 0x02, 212}, - {10, 0x02, 212}, - {15, 0x02, 212}, - {24, 0x02, 212}, - {31, 0x02, 212}, - {41, 0x02, 212}, - {56, 0x03, 212}, - }, - /* 212 */ - { - {3, 0x02, 214}, - {6, 0x02, 214}, - {10, 0x02, 214}, - {15, 0x02, 214}, - {24, 0x02, 214}, - {31, 0x02, 214}, - {41, 0x02, 214}, - {56, 0x03, 214}, - {3, 0x02, 221}, - {6, 0x02, 221}, - {10, 0x02, 221}, - {15, 0x02, 221}, - {24, 0x02, 221}, - {31, 0x02, 221}, - {41, 0x02, 221}, - {56, 0x03, 221}, - }, - /* 213 */ - { - {2, 0x02, 222}, - {9, 0x02, 222}, - {23, 0x02, 222}, - {40, 0x03, 222}, - {2, 0x02, 223}, - {9, 0x02, 223}, - {23, 0x02, 223}, - {40, 0x03, 223}, - {2, 0x02, 241}, - {9, 0x02, 241}, - {23, 0x02, 241}, - {40, 0x03, 241}, - {2, 0x02, 244}, - {9, 0x02, 244}, - {23, 0x02, 244}, - {40, 0x03, 244}, - }, - /* 214 */ - { - {3, 0x02, 222}, - {6, 0x02, 222}, - {10, 0x02, 222}, - {15, 0x02, 222}, - {24, 0x02, 222}, - {31, 0x02, 222}, - {41, 0x02, 222}, - {56, 0x03, 222}, - {3, 0x02, 223}, - {6, 0x02, 223}, - {10, 0x02, 223}, - {15, 0x02, 223}, - {24, 0x02, 223}, - {31, 0x02, 223}, - {41, 0x02, 223}, - {56, 0x03, 223}, - }, - /* 215 */ - { - {3, 0x02, 241}, - {6, 0x02, 241}, - {10, 0x02, 241}, - {15, 0x02, 241}, - {24, 0x02, 241}, - {31, 0x02, 241}, - {41, 0x02, 241}, - {56, 0x03, 241}, - {3, 0x02, 244}, - {6, 0x02, 244}, - {10, 0x02, 244}, - {15, 0x02, 244}, - {24, 0x02, 244}, - {31, 0x02, 244}, - {41, 0x02, 244}, - {56, 0x03, 244}, - }, - /* 216 */ - { - {1, 0x02, 245}, - {22, 0x03, 245}, - {1, 0x02, 246}, - {22, 0x03, 246}, - {1, 0x02, 247}, - {22, 0x03, 247}, - {1, 0x02, 248}, - {22, 0x03, 248}, - {1, 0x02, 250}, - {22, 0x03, 250}, - {1, 0x02, 251}, - {22, 0x03, 251}, - {1, 0x02, 252}, - {22, 0x03, 252}, - {1, 0x02, 253}, - {22, 0x03, 253}, - }, - /* 217 */ - { - {2, 0x02, 245}, - {9, 0x02, 245}, - {23, 0x02, 245}, - {40, 0x03, 245}, - {2, 0x02, 246}, - {9, 0x02, 246}, - {23, 0x02, 246}, - {40, 0x03, 246}, - {2, 0x02, 247}, - {9, 0x02, 247}, - {23, 0x02, 247}, - {40, 0x03, 247}, - {2, 0x02, 248}, - {9, 0x02, 248}, - {23, 0x02, 248}, - {40, 0x03, 248}, - }, - /* 218 */ - { - {3, 0x02, 245}, - {6, 0x02, 245}, - {10, 0x02, 245}, - {15, 0x02, 245}, - {24, 0x02, 245}, - {31, 0x02, 245}, - {41, 0x02, 245}, - {56, 0x03, 245}, - {3, 0x02, 246}, - {6, 0x02, 246}, - {10, 0x02, 246}, - {15, 0x02, 246}, - {24, 0x02, 246}, - {31, 0x02, 246}, - {41, 0x02, 246}, - {56, 0x03, 246}, - }, - /* 219 */ - { - {3, 0x02, 247}, - {6, 0x02, 247}, - {10, 0x02, 247}, - {15, 0x02, 247}, - {24, 0x02, 247}, - {31, 0x02, 247}, - {41, 0x02, 247}, - {56, 0x03, 247}, - {3, 0x02, 248}, - {6, 0x02, 248}, - {10, 0x02, 248}, - {15, 0x02, 248}, - {24, 0x02, 248}, - {31, 0x02, 248}, - {41, 0x02, 248}, - {56, 0x03, 248}, - }, - /* 220 */ - { - {2, 0x02, 250}, - {9, 0x02, 250}, - {23, 0x02, 250}, - {40, 0x03, 250}, - {2, 0x02, 251}, - {9, 0x02, 251}, - {23, 0x02, 251}, - {40, 0x03, 251}, - {2, 0x02, 252}, - {9, 0x02, 252}, - {23, 0x02, 252}, - {40, 0x03, 252}, - {2, 0x02, 253}, - {9, 0x02, 253}, - {23, 0x02, 253}, - {40, 0x03, 253}, - }, - /* 221 */ - { - {3, 0x02, 250}, - {6, 0x02, 250}, - {10, 0x02, 250}, - {15, 0x02, 250}, - {24, 0x02, 250}, - {31, 0x02, 250}, - {41, 0x02, 250}, - {56, 0x03, 250}, - {3, 0x02, 251}, - {6, 0x02, 251}, - {10, 0x02, 251}, - {15, 0x02, 251}, - {24, 0x02, 251}, - {31, 0x02, 251}, - {41, 0x02, 251}, - {56, 0x03, 251}, - }, - /* 222 */ - { - {3, 0x02, 252}, - {6, 0x02, 252}, - {10, 0x02, 252}, - {15, 0x02, 252}, - {24, 0x02, 252}, - {31, 0x02, 252}, - {41, 0x02, 252}, - {56, 0x03, 252}, - {3, 0x02, 253}, - {6, 0x02, 253}, - {10, 0x02, 253}, - {15, 0x02, 253}, - {24, 0x02, 253}, - {31, 0x02, 253}, - {41, 0x02, 253}, - {56, 0x03, 253}, - }, - /* 223 */ - { - {0, 0x03, 254}, - {227, 0x00, 0}, - {229, 0x00, 0}, - {230, 0x00, 0}, - {233, 0x00, 0}, - {234, 0x00, 0}, - {236, 0x00, 0}, - {237, 0x00, 0}, - {241, 0x00, 0}, - {242, 0x00, 0}, - {244, 0x00, 0}, - {245, 0x00, 0}, - {248, 0x00, 0}, - {249, 0x00, 0}, - {251, 0x00, 0}, - {252, 0x00, 0}, - }, - /* 224 */ - { - {1, 0x02, 254}, - {22, 0x03, 254}, - {0, 0x03, 2}, - {0, 0x03, 3}, - {0, 0x03, 4}, - {0, 0x03, 5}, - {0, 0x03, 6}, - {0, 0x03, 7}, - {0, 0x03, 8}, - {0, 0x03, 11}, - {0, 0x03, 12}, - {0, 0x03, 14}, - {0, 0x03, 15}, - {0, 0x03, 16}, - {0, 0x03, 17}, - {0, 0x03, 18}, - }, - /* 225 */ - { - {2, 0x02, 254}, - {9, 0x02, 254}, - {23, 0x02, 254}, - {40, 0x03, 254}, - {1, 0x02, 2}, - {22, 0x03, 2}, - {1, 0x02, 3}, - {22, 0x03, 3}, - {1, 0x02, 4}, - {22, 0x03, 4}, - {1, 0x02, 5}, - {22, 0x03, 5}, - {1, 0x02, 6}, - {22, 0x03, 6}, - {1, 0x02, 7}, - {22, 0x03, 7}, - }, - /* 226 */ - { - {3, 0x02, 254}, - {6, 0x02, 254}, - {10, 0x02, 254}, - {15, 0x02, 254}, - {24, 0x02, 254}, - {31, 0x02, 254}, - {41, 0x02, 254}, - {56, 0x03, 254}, - {2, 0x02, 2}, - {9, 0x02, 2}, - {23, 0x02, 2}, - {40, 0x03, 2}, - {2, 0x02, 3}, - {9, 0x02, 3}, - {23, 0x02, 3}, - {40, 0x03, 3}, - }, - /* 227 */ - { - {3, 0x02, 2}, - {6, 0x02, 2}, - {10, 0x02, 2}, - {15, 0x02, 2}, - {24, 0x02, 2}, - {31, 0x02, 2}, - {41, 0x02, 2}, - {56, 0x03, 2}, - {3, 0x02, 3}, - {6, 0x02, 3}, - {10, 0x02, 3}, - {15, 0x02, 3}, - {24, 0x02, 3}, - {31, 0x02, 3}, - {41, 0x02, 3}, - {56, 0x03, 3}, - }, - /* 228 */ - { - {2, 0x02, 4}, - {9, 0x02, 4}, - {23, 0x02, 4}, - {40, 0x03, 4}, - {2, 0x02, 5}, - {9, 0x02, 5}, - {23, 0x02, 5}, - {40, 0x03, 5}, - {2, 0x02, 6}, - {9, 0x02, 6}, - {23, 0x02, 6}, - {40, 0x03, 6}, - {2, 0x02, 7}, - {9, 0x02, 7}, - {23, 0x02, 7}, - {40, 0x03, 7}, - }, - /* 229 */ - { - {3, 0x02, 4}, - {6, 0x02, 4}, - {10, 0x02, 4}, - {15, 0x02, 4}, - {24, 0x02, 4}, - {31, 0x02, 4}, - {41, 0x02, 4}, - {56, 0x03, 4}, - {3, 0x02, 5}, - {6, 0x02, 5}, - {10, 0x02, 5}, - {15, 0x02, 5}, - {24, 0x02, 5}, - {31, 0x02, 5}, - {41, 0x02, 5}, - {56, 0x03, 5}, - }, - /* 230 */ - { - {3, 0x02, 6}, - {6, 0x02, 6}, - {10, 0x02, 6}, - {15, 0x02, 6}, - {24, 0x02, 6}, - {31, 0x02, 6}, - {41, 0x02, 6}, - {56, 0x03, 6}, - {3, 0x02, 7}, - {6, 0x02, 7}, - {10, 0x02, 7}, - {15, 0x02, 7}, - {24, 0x02, 7}, - {31, 0x02, 7}, - {41, 0x02, 7}, - {56, 0x03, 7}, - }, - /* 231 */ - { - {1, 0x02, 8}, - {22, 0x03, 8}, - {1, 0x02, 11}, - {22, 0x03, 11}, - {1, 0x02, 12}, - {22, 0x03, 12}, - {1, 0x02, 14}, - {22, 0x03, 14}, - {1, 0x02, 15}, - {22, 0x03, 15}, - {1, 0x02, 16}, - {22, 0x03, 16}, - {1, 0x02, 17}, - {22, 0x03, 17}, - {1, 0x02, 18}, - {22, 0x03, 18}, - }, - /* 232 */ - { - {2, 0x02, 8}, - {9, 0x02, 8}, - {23, 0x02, 8}, - {40, 0x03, 8}, - {2, 0x02, 11}, - {9, 0x02, 11}, - {23, 0x02, 11}, - {40, 0x03, 11}, - {2, 0x02, 12}, - {9, 0x02, 12}, - {23, 0x02, 12}, - {40, 0x03, 12}, - {2, 0x02, 14}, - {9, 0x02, 14}, - {23, 0x02, 14}, - {40, 0x03, 14}, - }, - /* 233 */ - { - {3, 0x02, 8}, - {6, 0x02, 8}, - {10, 0x02, 8}, - {15, 0x02, 8}, - {24, 0x02, 8}, - {31, 0x02, 8}, - {41, 0x02, 8}, - {56, 0x03, 8}, - {3, 0x02, 11}, - {6, 0x02, 11}, - {10, 0x02, 11}, - {15, 0x02, 11}, - {24, 0x02, 11}, - {31, 0x02, 11}, - {41, 0x02, 11}, - {56, 0x03, 11}, - }, - /* 234 */ - { - {3, 0x02, 12}, - {6, 0x02, 12}, - {10, 0x02, 12}, - {15, 0x02, 12}, - {24, 0x02, 12}, - {31, 0x02, 12}, - {41, 0x02, 12}, - {56, 0x03, 12}, - {3, 0x02, 14}, - {6, 0x02, 14}, - {10, 0x02, 14}, - {15, 0x02, 14}, - {24, 0x02, 14}, - {31, 0x02, 14}, - {41, 0x02, 14}, - {56, 0x03, 14}, - }, - /* 235 */ - { - {2, 0x02, 15}, - {9, 0x02, 15}, - {23, 0x02, 15}, - {40, 0x03, 15}, - {2, 0x02, 16}, - {9, 0x02, 16}, - {23, 0x02, 16}, - {40, 0x03, 16}, - {2, 0x02, 17}, - {9, 0x02, 17}, - {23, 0x02, 17}, - {40, 0x03, 17}, - {2, 0x02, 18}, - {9, 0x02, 18}, - {23, 0x02, 18}, - {40, 0x03, 18}, - }, - /* 236 */ - { - {3, 0x02, 15}, - {6, 0x02, 15}, - {10, 0x02, 15}, - {15, 0x02, 15}, - {24, 0x02, 15}, - {31, 0x02, 15}, - {41, 0x02, 15}, - {56, 0x03, 15}, - {3, 0x02, 16}, - {6, 0x02, 16}, - {10, 0x02, 16}, - {15, 0x02, 16}, - {24, 0x02, 16}, - {31, 0x02, 16}, - {41, 0x02, 16}, - {56, 0x03, 16}, - }, - /* 237 */ - { - {3, 0x02, 17}, - {6, 0x02, 17}, - {10, 0x02, 17}, - {15, 0x02, 17}, - {24, 0x02, 17}, - {31, 0x02, 17}, - {41, 0x02, 17}, - {56, 0x03, 17}, - {3, 0x02, 18}, - {6, 0x02, 18}, - {10, 0x02, 18}, - {15, 0x02, 18}, - {24, 0x02, 18}, - {31, 0x02, 18}, - {41, 0x02, 18}, - {56, 0x03, 18}, - }, - /* 238 */ - { - {0, 0x03, 19}, - {0, 0x03, 20}, - {0, 0x03, 21}, - {0, 0x03, 23}, - {0, 0x03, 24}, - {0, 0x03, 25}, - {0, 0x03, 26}, - {0, 0x03, 27}, - {0, 0x03, 28}, - {0, 0x03, 29}, - {0, 0x03, 30}, - {0, 0x03, 31}, - {0, 0x03, 127}, - {0, 0x03, 220}, - {0, 0x03, 249}, - {253, 0x00, 0}, - }, - /* 239 */ - { - {1, 0x02, 19}, - {22, 0x03, 19}, - {1, 0x02, 20}, - {22, 0x03, 20}, - {1, 0x02, 21}, - {22, 0x03, 21}, - {1, 0x02, 23}, - {22, 0x03, 23}, - {1, 0x02, 24}, - {22, 0x03, 24}, - {1, 0x02, 25}, - {22, 0x03, 25}, - {1, 0x02, 26}, - {22, 0x03, 26}, - {1, 0x02, 27}, - {22, 0x03, 27}, - }, - /* 240 */ - { - {2, 0x02, 19}, - {9, 0x02, 19}, - {23, 0x02, 19}, - {40, 0x03, 19}, - {2, 0x02, 20}, - {9, 0x02, 20}, - {23, 0x02, 20}, - {40, 0x03, 20}, - {2, 0x02, 21}, - {9, 0x02, 21}, - {23, 0x02, 21}, - {40, 0x03, 21}, - {2, 0x02, 23}, - {9, 0x02, 23}, - {23, 0x02, 23}, - {40, 0x03, 23}, - }, - /* 241 */ - { - {3, 0x02, 19}, - {6, 0x02, 19}, - {10, 0x02, 19}, - {15, 0x02, 19}, - {24, 0x02, 19}, - {31, 0x02, 19}, - {41, 0x02, 19}, - {56, 0x03, 19}, - {3, 0x02, 20}, - {6, 0x02, 20}, - {10, 0x02, 20}, - {15, 0x02, 20}, - {24, 0x02, 20}, - {31, 0x02, 20}, - {41, 0x02, 20}, - {56, 0x03, 20}, - }, - /* 242 */ - { - {3, 0x02, 21}, - {6, 0x02, 21}, - {10, 0x02, 21}, - {15, 0x02, 21}, - {24, 0x02, 21}, - {31, 0x02, 21}, - {41, 0x02, 21}, - {56, 0x03, 21}, - {3, 0x02, 23}, - {6, 0x02, 23}, - {10, 0x02, 23}, - {15, 0x02, 23}, - {24, 0x02, 23}, - {31, 0x02, 23}, - {41, 0x02, 23}, - {56, 0x03, 23}, - }, - /* 243 */ - { - {2, 0x02, 24}, - {9, 0x02, 24}, - {23, 0x02, 24}, - {40, 0x03, 24}, - {2, 0x02, 25}, - {9, 0x02, 25}, - {23, 0x02, 25}, - {40, 0x03, 25}, - {2, 0x02, 26}, - {9, 0x02, 26}, - {23, 0x02, 26}, - {40, 0x03, 26}, - {2, 0x02, 27}, - {9, 0x02, 27}, - {23, 0x02, 27}, - {40, 0x03, 27}, - }, - /* 244 */ - { - {3, 0x02, 24}, - {6, 0x02, 24}, - {10, 0x02, 24}, - {15, 0x02, 24}, - {24, 0x02, 24}, - {31, 0x02, 24}, - {41, 0x02, 24}, - {56, 0x03, 24}, - {3, 0x02, 25}, - {6, 0x02, 25}, - {10, 0x02, 25}, - {15, 0x02, 25}, - {24, 0x02, 25}, - {31, 0x02, 25}, - {41, 0x02, 25}, - {56, 0x03, 25}, - }, - /* 245 */ - { - {3, 0x02, 26}, - {6, 0x02, 26}, - {10, 0x02, 26}, - {15, 0x02, 26}, - {24, 0x02, 26}, - {31, 0x02, 26}, - {41, 0x02, 26}, - {56, 0x03, 26}, - {3, 0x02, 27}, - {6, 0x02, 27}, - {10, 0x02, 27}, - {15, 0x02, 27}, - {24, 0x02, 27}, - {31, 0x02, 27}, - {41, 0x02, 27}, - {56, 0x03, 27}, - }, - /* 246 */ - { - {1, 0x02, 28}, - {22, 0x03, 28}, - {1, 0x02, 29}, - {22, 0x03, 29}, - {1, 0x02, 30}, - {22, 0x03, 30}, - {1, 0x02, 31}, - {22, 0x03, 31}, - {1, 0x02, 127}, - {22, 0x03, 127}, - {1, 0x02, 220}, - {22, 0x03, 220}, - {1, 0x02, 249}, - {22, 0x03, 249}, - {254, 0x00, 0}, - {255, 0x00, 0}, - }, - /* 247 */ - { - {2, 0x02, 28}, - {9, 0x02, 28}, - {23, 0x02, 28}, - {40, 0x03, 28}, - {2, 0x02, 29}, - {9, 0x02, 29}, - {23, 0x02, 29}, - {40, 0x03, 29}, - {2, 0x02, 30}, - {9, 0x02, 30}, - {23, 0x02, 30}, - {40, 0x03, 30}, - {2, 0x02, 31}, - {9, 0x02, 31}, - {23, 0x02, 31}, - {40, 0x03, 31}, - }, - /* 248 */ - { - {3, 0x02, 28}, - {6, 0x02, 28}, - {10, 0x02, 28}, - {15, 0x02, 28}, - {24, 0x02, 28}, - {31, 0x02, 28}, - {41, 0x02, 28}, - {56, 0x03, 28}, - {3, 0x02, 29}, - {6, 0x02, 29}, - {10, 0x02, 29}, - {15, 0x02, 29}, - {24, 0x02, 29}, - {31, 0x02, 29}, - {41, 0x02, 29}, - {56, 0x03, 29}, - }, - /* 249 */ - { - {3, 0x02, 30}, - {6, 0x02, 30}, - {10, 0x02, 30}, - {15, 0x02, 30}, - {24, 0x02, 30}, - {31, 0x02, 30}, - {41, 0x02, 30}, - {56, 0x03, 30}, - {3, 0x02, 31}, - {6, 0x02, 31}, - {10, 0x02, 31}, - {15, 0x02, 31}, - {24, 0x02, 31}, - {31, 0x02, 31}, - {41, 0x02, 31}, - {56, 0x03, 31}, - }, - /* 250 */ - { - {2, 0x02, 127}, - {9, 0x02, 127}, - {23, 0x02, 127}, - {40, 0x03, 127}, - {2, 0x02, 220}, - {9, 0x02, 220}, - {23, 0x02, 220}, - {40, 0x03, 220}, - {2, 0x02, 249}, - {9, 0x02, 249}, - {23, 0x02, 249}, - {40, 0x03, 249}, - {0, 0x03, 10}, - {0, 0x03, 13}, - {0, 0x03, 22}, - {0, 0x04, 0}, - }, - /* 251 */ - { - {3, 0x02, 127}, - {6, 0x02, 127}, - {10, 0x02, 127}, - {15, 0x02, 127}, - {24, 0x02, 127}, - {31, 0x02, 127}, - {41, 0x02, 127}, - {56, 0x03, 127}, - {3, 0x02, 220}, - {6, 0x02, 220}, - {10, 0x02, 220}, - {15, 0x02, 220}, - {24, 0x02, 220}, - {31, 0x02, 220}, - {41, 0x02, 220}, - {56, 0x03, 220}, - }, - /* 252 */ - { - {3, 0x02, 249}, - {6, 0x02, 249}, - {10, 0x02, 249}, - {15, 0x02, 249}, - {24, 0x02, 249}, - {31, 0x02, 249}, - {41, 0x02, 249}, - {56, 0x03, 249}, - {1, 0x02, 10}, - {22, 0x03, 10}, - {1, 0x02, 13}, - {22, 0x03, 13}, - {1, 0x02, 22}, - {22, 0x03, 22}, - {0, 0x04, 0}, - {0, 0x04, 0}, - }, - /* 253 */ - { - {2, 0x02, 10}, - {9, 0x02, 10}, - {23, 0x02, 10}, - {40, 0x03, 10}, - {2, 0x02, 13}, - {9, 0x02, 13}, - {23, 0x02, 13}, - {40, 0x03, 13}, - {2, 0x02, 22}, - {9, 0x02, 22}, - {23, 0x02, 22}, - {40, 0x03, 22}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - }, - /* 254 */ - { - {3, 0x02, 10}, - {6, 0x02, 10}, - {10, 0x02, 10}, - {15, 0x02, 10}, - {24, 0x02, 10}, - {31, 0x02, 10}, - {41, 0x02, 10}, - {56, 0x03, 10}, - {3, 0x02, 13}, - {6, 0x02, 13}, - {10, 0x02, 13}, - {15, 0x02, 13}, - {24, 0x02, 13}, - {31, 0x02, 13}, - {41, 0x02, 13}, - {56, 0x03, 13}, - }, - /* 255 */ - { - {3, 0x02, 22}, - {6, 0x02, 22}, - {10, 0x02, 22}, - {15, 0x02, 22}, - {24, 0x02, 22}, - {31, 0x02, 22}, - {41, 0x02, 22}, - {56, 0x03, 22}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - {0, 0x04, 0}, - }, + /* 0 */ + { + {0x04, 0}, + {0x05, 0}, + {0x07, 0}, + {0x08, 0}, + {0x0b, 0}, + {0x0c, 0}, + {0x10, 0}, + {0x13, 0}, + {0x19, 0}, + {0x1c, 0}, + {0x20, 0}, + {0x23, 0}, + {0x2a, 0}, + {0x31, 0}, + {0x39, 0}, + {0x4040, 0}, + }, + /* 1 */ + { + {0xc000, 48}, + {0xc000, 49}, + {0xc000, 50}, + {0xc000, 97}, + {0xc000, 99}, + {0xc000, 101}, + {0xc000, 105}, + {0xc000, 111}, + {0xc000, 115}, + {0xc000, 116}, + {0x0d, 0}, + {0x0e, 0}, + {0x11, 0}, + {0x12, 0}, + {0x14, 0}, + {0x15, 0}, + }, + /* 2 */ + { + {0x8001, 48}, + {0xc016, 48}, + {0x8001, 49}, + {0xc016, 49}, + {0x8001, 50}, + {0xc016, 50}, + {0x8001, 97}, + {0xc016, 97}, + {0x8001, 99}, + {0xc016, 99}, + {0x8001, 101}, + {0xc016, 101}, + {0x8001, 105}, + {0xc016, 105}, + {0x8001, 111}, + {0xc016, 111}, + }, + /* 3 */ + { + {0x8002, 48}, + {0x8009, 48}, + {0x8017, 48}, + {0xc028, 48}, + {0x8002, 49}, + {0x8009, 49}, + {0x8017, 49}, + {0xc028, 49}, + {0x8002, 50}, + {0x8009, 50}, + {0x8017, 50}, + {0xc028, 50}, + {0x8002, 97}, + {0x8009, 97}, + {0x8017, 97}, + {0xc028, 97}, + }, + /* 4 */ + { + {0x8003, 48}, + {0x8006, 48}, + {0x800a, 48}, + {0x800f, 48}, + {0x8018, 48}, + {0x801f, 48}, + {0x8029, 48}, + {0xc038, 48}, + {0x8003, 49}, + {0x8006, 49}, + {0x800a, 49}, + {0x800f, 49}, + {0x8018, 49}, + {0x801f, 49}, + {0x8029, 49}, + {0xc038, 49}, + }, + /* 5 */ + { + {0x8003, 50}, + {0x8006, 50}, + {0x800a, 50}, + {0x800f, 50}, + {0x8018, 50}, + {0x801f, 50}, + {0x8029, 50}, + {0xc038, 50}, + {0x8003, 97}, + {0x8006, 97}, + {0x800a, 97}, + {0x800f, 97}, + {0x8018, 97}, + {0x801f, 97}, + {0x8029, 97}, + {0xc038, 97}, + }, + /* 6 */ + { + {0x8002, 99}, + {0x8009, 99}, + {0x8017, 99}, + {0xc028, 99}, + {0x8002, 101}, + {0x8009, 101}, + {0x8017, 101}, + {0xc028, 101}, + {0x8002, 105}, + {0x8009, 105}, + {0x8017, 105}, + {0xc028, 105}, + {0x8002, 111}, + {0x8009, 111}, + {0x8017, 111}, + {0xc028, 111}, + }, + /* 7 */ + { + {0x8003, 99}, + {0x8006, 99}, + {0x800a, 99}, + {0x800f, 99}, + {0x8018, 99}, + {0x801f, 99}, + {0x8029, 99}, + {0xc038, 99}, + {0x8003, 101}, + {0x8006, 101}, + {0x800a, 101}, + {0x800f, 101}, + {0x8018, 101}, + {0x801f, 101}, + {0x8029, 101}, + {0xc038, 101}, + }, + /* 8 */ + { + {0x8003, 105}, + {0x8006, 105}, + {0x800a, 105}, + {0x800f, 105}, + {0x8018, 105}, + {0x801f, 105}, + {0x8029, 105}, + {0xc038, 105}, + {0x8003, 111}, + {0x8006, 111}, + {0x800a, 111}, + {0x800f, 111}, + {0x8018, 111}, + {0x801f, 111}, + {0x8029, 111}, + {0xc038, 111}, + }, + /* 9 */ + { + {0x8001, 115}, + {0xc016, 115}, + {0x8001, 116}, + {0xc016, 116}, + {0xc000, 32}, + {0xc000, 37}, + {0xc000, 45}, + {0xc000, 46}, + {0xc000, 47}, + {0xc000, 51}, + {0xc000, 52}, + {0xc000, 53}, + {0xc000, 54}, + {0xc000, 55}, + {0xc000, 56}, + {0xc000, 57}, + }, + /* 10 */ + { + {0x8002, 115}, + {0x8009, 115}, + {0x8017, 115}, + {0xc028, 115}, + {0x8002, 116}, + {0x8009, 116}, + {0x8017, 116}, + {0xc028, 116}, + {0x8001, 32}, + {0xc016, 32}, + {0x8001, 37}, + {0xc016, 37}, + {0x8001, 45}, + {0xc016, 45}, + {0x8001, 46}, + {0xc016, 46}, + }, + /* 11 */ + { + {0x8003, 115}, + {0x8006, 115}, + {0x800a, 115}, + {0x800f, 115}, + {0x8018, 115}, + {0x801f, 115}, + {0x8029, 115}, + {0xc038, 115}, + {0x8003, 116}, + {0x8006, 116}, + {0x800a, 116}, + {0x800f, 116}, + {0x8018, 116}, + {0x801f, 116}, + {0x8029, 116}, + {0xc038, 116}, + }, + /* 12 */ + { + {0x8002, 32}, + {0x8009, 32}, + {0x8017, 32}, + {0xc028, 32}, + {0x8002, 37}, + {0x8009, 37}, + {0x8017, 37}, + {0xc028, 37}, + {0x8002, 45}, + {0x8009, 45}, + {0x8017, 45}, + {0xc028, 45}, + {0x8002, 46}, + {0x8009, 46}, + {0x8017, 46}, + {0xc028, 46}, + }, + /* 13 */ + { + {0x8003, 32}, + {0x8006, 32}, + {0x800a, 32}, + {0x800f, 32}, + {0x8018, 32}, + {0x801f, 32}, + {0x8029, 32}, + {0xc038, 32}, + {0x8003, 37}, + {0x8006, 37}, + {0x800a, 37}, + {0x800f, 37}, + {0x8018, 37}, + {0x801f, 37}, + {0x8029, 37}, + {0xc038, 37}, + }, + /* 14 */ + { + {0x8003, 45}, + {0x8006, 45}, + {0x800a, 45}, + {0x800f, 45}, + {0x8018, 45}, + {0x801f, 45}, + {0x8029, 45}, + {0xc038, 45}, + {0x8003, 46}, + {0x8006, 46}, + {0x800a, 46}, + {0x800f, 46}, + {0x8018, 46}, + {0x801f, 46}, + {0x8029, 46}, + {0xc038, 46}, + }, + /* 15 */ + { + {0x8001, 47}, + {0xc016, 47}, + {0x8001, 51}, + {0xc016, 51}, + {0x8001, 52}, + {0xc016, 52}, + {0x8001, 53}, + {0xc016, 53}, + {0x8001, 54}, + {0xc016, 54}, + {0x8001, 55}, + {0xc016, 55}, + {0x8001, 56}, + {0xc016, 56}, + {0x8001, 57}, + {0xc016, 57}, + }, + /* 16 */ + { + {0x8002, 47}, + {0x8009, 47}, + {0x8017, 47}, + {0xc028, 47}, + {0x8002, 51}, + {0x8009, 51}, + {0x8017, 51}, + {0xc028, 51}, + {0x8002, 52}, + {0x8009, 52}, + {0x8017, 52}, + {0xc028, 52}, + {0x8002, 53}, + {0x8009, 53}, + {0x8017, 53}, + {0xc028, 53}, + }, + /* 17 */ + { + {0x8003, 47}, + {0x8006, 47}, + {0x800a, 47}, + {0x800f, 47}, + {0x8018, 47}, + {0x801f, 47}, + {0x8029, 47}, + {0xc038, 47}, + {0x8003, 51}, + {0x8006, 51}, + {0x800a, 51}, + {0x800f, 51}, + {0x8018, 51}, + {0x801f, 51}, + {0x8029, 51}, + {0xc038, 51}, + }, + /* 18 */ + { + {0x8003, 52}, + {0x8006, 52}, + {0x800a, 52}, + {0x800f, 52}, + {0x8018, 52}, + {0x801f, 52}, + {0x8029, 52}, + {0xc038, 52}, + {0x8003, 53}, + {0x8006, 53}, + {0x800a, 53}, + {0x800f, 53}, + {0x8018, 53}, + {0x801f, 53}, + {0x8029, 53}, + {0xc038, 53}, + }, + /* 19 */ + { + {0x8002, 54}, + {0x8009, 54}, + {0x8017, 54}, + {0xc028, 54}, + {0x8002, 55}, + {0x8009, 55}, + {0x8017, 55}, + {0xc028, 55}, + {0x8002, 56}, + {0x8009, 56}, + {0x8017, 56}, + {0xc028, 56}, + {0x8002, 57}, + {0x8009, 57}, + {0x8017, 57}, + {0xc028, 57}, + }, + /* 20 */ + { + {0x8003, 54}, + {0x8006, 54}, + {0x800a, 54}, + {0x800f, 54}, + {0x8018, 54}, + {0x801f, 54}, + {0x8029, 54}, + {0xc038, 54}, + {0x8003, 55}, + {0x8006, 55}, + {0x800a, 55}, + {0x800f, 55}, + {0x8018, 55}, + {0x801f, 55}, + {0x8029, 55}, + {0xc038, 55}, + }, + /* 21 */ + { + {0x8003, 56}, + {0x8006, 56}, + {0x800a, 56}, + {0x800f, 56}, + {0x8018, 56}, + {0x801f, 56}, + {0x8029, 56}, + {0xc038, 56}, + {0x8003, 57}, + {0x8006, 57}, + {0x800a, 57}, + {0x800f, 57}, + {0x8018, 57}, + {0x801f, 57}, + {0x8029, 57}, + {0xc038, 57}, + }, + /* 22 */ + { + {0x1a, 0}, + {0x1b, 0}, + {0x1d, 0}, + {0x1e, 0}, + {0x21, 0}, + {0x22, 0}, + {0x24, 0}, + {0x25, 0}, + {0x2b, 0}, + {0x2e, 0}, + {0x32, 0}, + {0x35, 0}, + {0x3a, 0}, + {0x3d, 0}, + {0x41, 0}, + {0x4044, 0}, + }, + /* 23 */ + { + {0xc000, 61}, + {0xc000, 65}, + {0xc000, 95}, + {0xc000, 98}, + {0xc000, 100}, + {0xc000, 102}, + {0xc000, 103}, + {0xc000, 104}, + {0xc000, 108}, + {0xc000, 109}, + {0xc000, 110}, + {0xc000, 112}, + {0xc000, 114}, + {0xc000, 117}, + {0x26, 0}, + {0x27, 0}, + }, + /* 24 */ + { + {0x8001, 61}, + {0xc016, 61}, + {0x8001, 65}, + {0xc016, 65}, + {0x8001, 95}, + {0xc016, 95}, + {0x8001, 98}, + {0xc016, 98}, + {0x8001, 100}, + {0xc016, 100}, + {0x8001, 102}, + {0xc016, 102}, + {0x8001, 103}, + {0xc016, 103}, + {0x8001, 104}, + {0xc016, 104}, + }, + /* 25 */ + { + {0x8002, 61}, + {0x8009, 61}, + {0x8017, 61}, + {0xc028, 61}, + {0x8002, 65}, + {0x8009, 65}, + {0x8017, 65}, + {0xc028, 65}, + {0x8002, 95}, + {0x8009, 95}, + {0x8017, 95}, + {0xc028, 95}, + {0x8002, 98}, + {0x8009, 98}, + {0x8017, 98}, + {0xc028, 98}, + }, + /* 26 */ + { + {0x8003, 61}, + {0x8006, 61}, + {0x800a, 61}, + {0x800f, 61}, + {0x8018, 61}, + {0x801f, 61}, + {0x8029, 61}, + {0xc038, 61}, + {0x8003, 65}, + {0x8006, 65}, + {0x800a, 65}, + {0x800f, 65}, + {0x8018, 65}, + {0x801f, 65}, + {0x8029, 65}, + {0xc038, 65}, + }, + /* 27 */ + { + {0x8003, 95}, + {0x8006, 95}, + {0x800a, 95}, + {0x800f, 95}, + {0x8018, 95}, + {0x801f, 95}, + {0x8029, 95}, + {0xc038, 95}, + {0x8003, 98}, + {0x8006, 98}, + {0x800a, 98}, + {0x800f, 98}, + {0x8018, 98}, + {0x801f, 98}, + {0x8029, 98}, + {0xc038, 98}, + }, + /* 28 */ + { + {0x8002, 100}, + {0x8009, 100}, + {0x8017, 100}, + {0xc028, 100}, + {0x8002, 102}, + {0x8009, 102}, + {0x8017, 102}, + {0xc028, 102}, + {0x8002, 103}, + {0x8009, 103}, + {0x8017, 103}, + {0xc028, 103}, + {0x8002, 104}, + {0x8009, 104}, + {0x8017, 104}, + {0xc028, 104}, + }, + /* 29 */ + { + {0x8003, 100}, + {0x8006, 100}, + {0x800a, 100}, + {0x800f, 100}, + {0x8018, 100}, + {0x801f, 100}, + {0x8029, 100}, + {0xc038, 100}, + {0x8003, 102}, + {0x8006, 102}, + {0x800a, 102}, + {0x800f, 102}, + {0x8018, 102}, + {0x801f, 102}, + {0x8029, 102}, + {0xc038, 102}, + }, + /* 30 */ + { + {0x8003, 103}, + {0x8006, 103}, + {0x800a, 103}, + {0x800f, 103}, + {0x8018, 103}, + {0x801f, 103}, + {0x8029, 103}, + {0xc038, 103}, + {0x8003, 104}, + {0x8006, 104}, + {0x800a, 104}, + {0x800f, 104}, + {0x8018, 104}, + {0x801f, 104}, + {0x8029, 104}, + {0xc038, 104}, + }, + /* 31 */ + { + {0x8001, 108}, + {0xc016, 108}, + {0x8001, 109}, + {0xc016, 109}, + {0x8001, 110}, + {0xc016, 110}, + {0x8001, 112}, + {0xc016, 112}, + {0x8001, 114}, + {0xc016, 114}, + {0x8001, 117}, + {0xc016, 117}, + {0xc000, 58}, + {0xc000, 66}, + {0xc000, 67}, + {0xc000, 68}, + }, + /* 32 */ + { + {0x8002, 108}, + {0x8009, 108}, + {0x8017, 108}, + {0xc028, 108}, + {0x8002, 109}, + {0x8009, 109}, + {0x8017, 109}, + {0xc028, 109}, + {0x8002, 110}, + {0x8009, 110}, + {0x8017, 110}, + {0xc028, 110}, + {0x8002, 112}, + {0x8009, 112}, + {0x8017, 112}, + {0xc028, 112}, + }, + /* 33 */ + { + {0x8003, 108}, + {0x8006, 108}, + {0x800a, 108}, + {0x800f, 108}, + {0x8018, 108}, + {0x801f, 108}, + {0x8029, 108}, + {0xc038, 108}, + {0x8003, 109}, + {0x8006, 109}, + {0x800a, 109}, + {0x800f, 109}, + {0x8018, 109}, + {0x801f, 109}, + {0x8029, 109}, + {0xc038, 109}, + }, + /* 34 */ + { + {0x8003, 110}, + {0x8006, 110}, + {0x800a, 110}, + {0x800f, 110}, + {0x8018, 110}, + {0x801f, 110}, + {0x8029, 110}, + {0xc038, 110}, + {0x8003, 112}, + {0x8006, 112}, + {0x800a, 112}, + {0x800f, 112}, + {0x8018, 112}, + {0x801f, 112}, + {0x8029, 112}, + {0xc038, 112}, + }, + /* 35 */ + { + {0x8002, 114}, + {0x8009, 114}, + {0x8017, 114}, + {0xc028, 114}, + {0x8002, 117}, + {0x8009, 117}, + {0x8017, 117}, + {0xc028, 117}, + {0x8001, 58}, + {0xc016, 58}, + {0x8001, 66}, + {0xc016, 66}, + {0x8001, 67}, + {0xc016, 67}, + {0x8001, 68}, + {0xc016, 68}, + }, + /* 36 */ + { + {0x8003, 114}, + {0x8006, 114}, + {0x800a, 114}, + {0x800f, 114}, + {0x8018, 114}, + {0x801f, 114}, + {0x8029, 114}, + {0xc038, 114}, + {0x8003, 117}, + {0x8006, 117}, + {0x800a, 117}, + {0x800f, 117}, + {0x8018, 117}, + {0x801f, 117}, + {0x8029, 117}, + {0xc038, 117}, + }, + /* 37 */ + { + {0x8002, 58}, + {0x8009, 58}, + {0x8017, 58}, + {0xc028, 58}, + {0x8002, 66}, + {0x8009, 66}, + {0x8017, 66}, + {0xc028, 66}, + {0x8002, 67}, + {0x8009, 67}, + {0x8017, 67}, + {0xc028, 67}, + {0x8002, 68}, + {0x8009, 68}, + {0x8017, 68}, + {0xc028, 68}, + }, + /* 38 */ + { + {0x8003, 58}, + {0x8006, 58}, + {0x800a, 58}, + {0x800f, 58}, + {0x8018, 58}, + {0x801f, 58}, + {0x8029, 58}, + {0xc038, 58}, + {0x8003, 66}, + {0x8006, 66}, + {0x800a, 66}, + {0x800f, 66}, + {0x8018, 66}, + {0x801f, 66}, + {0x8029, 66}, + {0xc038, 66}, + }, + /* 39 */ + { + {0x8003, 67}, + {0x8006, 67}, + {0x800a, 67}, + {0x800f, 67}, + {0x8018, 67}, + {0x801f, 67}, + {0x8029, 67}, + {0xc038, 67}, + {0x8003, 68}, + {0x8006, 68}, + {0x800a, 68}, + {0x800f, 68}, + {0x8018, 68}, + {0x801f, 68}, + {0x8029, 68}, + {0xc038, 68}, + }, + /* 40 */ + { + {0x2c, 0}, + {0x2d, 0}, + {0x2f, 0}, + {0x30, 0}, + {0x33, 0}, + {0x34, 0}, + {0x36, 0}, + {0x37, 0}, + {0x3b, 0}, + {0x3c, 0}, + {0x3e, 0}, + {0x3f, 0}, + {0x42, 0}, + {0x43, 0}, + {0x45, 0}, + {0x4048, 0}, + }, + /* 41 */ + { + {0xc000, 69}, + {0xc000, 70}, + {0xc000, 71}, + {0xc000, 72}, + {0xc000, 73}, + {0xc000, 74}, + {0xc000, 75}, + {0xc000, 76}, + {0xc000, 77}, + {0xc000, 78}, + {0xc000, 79}, + {0xc000, 80}, + {0xc000, 81}, + {0xc000, 82}, + {0xc000, 83}, + {0xc000, 84}, + }, + /* 42 */ + { + {0x8001, 69}, + {0xc016, 69}, + {0x8001, 70}, + {0xc016, 70}, + {0x8001, 71}, + {0xc016, 71}, + {0x8001, 72}, + {0xc016, 72}, + {0x8001, 73}, + {0xc016, 73}, + {0x8001, 74}, + {0xc016, 74}, + {0x8001, 75}, + {0xc016, 75}, + {0x8001, 76}, + {0xc016, 76}, + }, + /* 43 */ + { + {0x8002, 69}, + {0x8009, 69}, + {0x8017, 69}, + {0xc028, 69}, + {0x8002, 70}, + {0x8009, 70}, + {0x8017, 70}, + {0xc028, 70}, + {0x8002, 71}, + {0x8009, 71}, + {0x8017, 71}, + {0xc028, 71}, + {0x8002, 72}, + {0x8009, 72}, + {0x8017, 72}, + {0xc028, 72}, + }, + /* 44 */ + { + {0x8003, 69}, + {0x8006, 69}, + {0x800a, 69}, + {0x800f, 69}, + {0x8018, 69}, + {0x801f, 69}, + {0x8029, 69}, + {0xc038, 69}, + {0x8003, 70}, + {0x8006, 70}, + {0x800a, 70}, + {0x800f, 70}, + {0x8018, 70}, + {0x801f, 70}, + {0x8029, 70}, + {0xc038, 70}, + }, + /* 45 */ + { + {0x8003, 71}, + {0x8006, 71}, + {0x800a, 71}, + {0x800f, 71}, + {0x8018, 71}, + {0x801f, 71}, + {0x8029, 71}, + {0xc038, 71}, + {0x8003, 72}, + {0x8006, 72}, + {0x800a, 72}, + {0x800f, 72}, + {0x8018, 72}, + {0x801f, 72}, + {0x8029, 72}, + {0xc038, 72}, + }, + /* 46 */ + { + {0x8002, 73}, + {0x8009, 73}, + {0x8017, 73}, + {0xc028, 73}, + {0x8002, 74}, + {0x8009, 74}, + {0x8017, 74}, + {0xc028, 74}, + {0x8002, 75}, + {0x8009, 75}, + {0x8017, 75}, + {0xc028, 75}, + {0x8002, 76}, + {0x8009, 76}, + {0x8017, 76}, + {0xc028, 76}, + }, + /* 47 */ + { + {0x8003, 73}, + {0x8006, 73}, + {0x800a, 73}, + {0x800f, 73}, + {0x8018, 73}, + {0x801f, 73}, + {0x8029, 73}, + {0xc038, 73}, + {0x8003, 74}, + {0x8006, 74}, + {0x800a, 74}, + {0x800f, 74}, + {0x8018, 74}, + {0x801f, 74}, + {0x8029, 74}, + {0xc038, 74}, + }, + /* 48 */ + { + {0x8003, 75}, + {0x8006, 75}, + {0x800a, 75}, + {0x800f, 75}, + {0x8018, 75}, + {0x801f, 75}, + {0x8029, 75}, + {0xc038, 75}, + {0x8003, 76}, + {0x8006, 76}, + {0x800a, 76}, + {0x800f, 76}, + {0x8018, 76}, + {0x801f, 76}, + {0x8029, 76}, + {0xc038, 76}, + }, + /* 49 */ + { + {0x8001, 77}, + {0xc016, 77}, + {0x8001, 78}, + {0xc016, 78}, + {0x8001, 79}, + {0xc016, 79}, + {0x8001, 80}, + {0xc016, 80}, + {0x8001, 81}, + {0xc016, 81}, + {0x8001, 82}, + {0xc016, 82}, + {0x8001, 83}, + {0xc016, 83}, + {0x8001, 84}, + {0xc016, 84}, + }, + /* 50 */ + { + {0x8002, 77}, + {0x8009, 77}, + {0x8017, 77}, + {0xc028, 77}, + {0x8002, 78}, + {0x8009, 78}, + {0x8017, 78}, + {0xc028, 78}, + {0x8002, 79}, + {0x8009, 79}, + {0x8017, 79}, + {0xc028, 79}, + {0x8002, 80}, + {0x8009, 80}, + {0x8017, 80}, + {0xc028, 80}, + }, + /* 51 */ + { + {0x8003, 77}, + {0x8006, 77}, + {0x800a, 77}, + {0x800f, 77}, + {0x8018, 77}, + {0x801f, 77}, + {0x8029, 77}, + {0xc038, 77}, + {0x8003, 78}, + {0x8006, 78}, + {0x800a, 78}, + {0x800f, 78}, + {0x8018, 78}, + {0x801f, 78}, + {0x8029, 78}, + {0xc038, 78}, + }, + /* 52 */ + { + {0x8003, 79}, + {0x8006, 79}, + {0x800a, 79}, + {0x800f, 79}, + {0x8018, 79}, + {0x801f, 79}, + {0x8029, 79}, + {0xc038, 79}, + {0x8003, 80}, + {0x8006, 80}, + {0x800a, 80}, + {0x800f, 80}, + {0x8018, 80}, + {0x801f, 80}, + {0x8029, 80}, + {0xc038, 80}, + }, + /* 53 */ + { + {0x8002, 81}, + {0x8009, 81}, + {0x8017, 81}, + {0xc028, 81}, + {0x8002, 82}, + {0x8009, 82}, + {0x8017, 82}, + {0xc028, 82}, + {0x8002, 83}, + {0x8009, 83}, + {0x8017, 83}, + {0xc028, 83}, + {0x8002, 84}, + {0x8009, 84}, + {0x8017, 84}, + {0xc028, 84}, + }, + /* 54 */ + { + {0x8003, 81}, + {0x8006, 81}, + {0x800a, 81}, + {0x800f, 81}, + {0x8018, 81}, + {0x801f, 81}, + {0x8029, 81}, + {0xc038, 81}, + {0x8003, 82}, + {0x8006, 82}, + {0x800a, 82}, + {0x800f, 82}, + {0x8018, 82}, + {0x801f, 82}, + {0x8029, 82}, + {0xc038, 82}, + }, + /* 55 */ + { + {0x8003, 83}, + {0x8006, 83}, + {0x800a, 83}, + {0x800f, 83}, + {0x8018, 83}, + {0x801f, 83}, + {0x8029, 83}, + {0xc038, 83}, + {0x8003, 84}, + {0x8006, 84}, + {0x800a, 84}, + {0x800f, 84}, + {0x8018, 84}, + {0x801f, 84}, + {0x8029, 84}, + {0xc038, 84}, + }, + /* 56 */ + { + {0xc000, 85}, + {0xc000, 86}, + {0xc000, 87}, + {0xc000, 89}, + {0xc000, 106}, + {0xc000, 107}, + {0xc000, 113}, + {0xc000, 118}, + {0xc000, 119}, + {0xc000, 120}, + {0xc000, 121}, + {0xc000, 122}, + {0x46, 0}, + {0x47, 0}, + {0x49, 0}, + {0x404a, 0}, + }, + /* 57 */ + { + {0x8001, 85}, + {0xc016, 85}, + {0x8001, 86}, + {0xc016, 86}, + {0x8001, 87}, + {0xc016, 87}, + {0x8001, 89}, + {0xc016, 89}, + {0x8001, 106}, + {0xc016, 106}, + {0x8001, 107}, + {0xc016, 107}, + {0x8001, 113}, + {0xc016, 113}, + {0x8001, 118}, + {0xc016, 118}, + }, + /* 58 */ + { + {0x8002, 85}, + {0x8009, 85}, + {0x8017, 85}, + {0xc028, 85}, + {0x8002, 86}, + {0x8009, 86}, + {0x8017, 86}, + {0xc028, 86}, + {0x8002, 87}, + {0x8009, 87}, + {0x8017, 87}, + {0xc028, 87}, + {0x8002, 89}, + {0x8009, 89}, + {0x8017, 89}, + {0xc028, 89}, + }, + /* 59 */ + { + {0x8003, 85}, + {0x8006, 85}, + {0x800a, 85}, + {0x800f, 85}, + {0x8018, 85}, + {0x801f, 85}, + {0x8029, 85}, + {0xc038, 85}, + {0x8003, 86}, + {0x8006, 86}, + {0x800a, 86}, + {0x800f, 86}, + {0x8018, 86}, + {0x801f, 86}, + {0x8029, 86}, + {0xc038, 86}, + }, + /* 60 */ + { + {0x8003, 87}, + {0x8006, 87}, + {0x800a, 87}, + {0x800f, 87}, + {0x8018, 87}, + {0x801f, 87}, + {0x8029, 87}, + {0xc038, 87}, + {0x8003, 89}, + {0x8006, 89}, + {0x800a, 89}, + {0x800f, 89}, + {0x8018, 89}, + {0x801f, 89}, + {0x8029, 89}, + {0xc038, 89}, + }, + /* 61 */ + { + {0x8002, 106}, + {0x8009, 106}, + {0x8017, 106}, + {0xc028, 106}, + {0x8002, 107}, + {0x8009, 107}, + {0x8017, 107}, + {0xc028, 107}, + {0x8002, 113}, + {0x8009, 113}, + {0x8017, 113}, + {0xc028, 113}, + {0x8002, 118}, + {0x8009, 118}, + {0x8017, 118}, + {0xc028, 118}, + }, + /* 62 */ + { + {0x8003, 106}, + {0x8006, 106}, + {0x800a, 106}, + {0x800f, 106}, + {0x8018, 106}, + {0x801f, 106}, + {0x8029, 106}, + {0xc038, 106}, + {0x8003, 107}, + {0x8006, 107}, + {0x800a, 107}, + {0x800f, 107}, + {0x8018, 107}, + {0x801f, 107}, + {0x8029, 107}, + {0xc038, 107}, + }, + /* 63 */ + { + {0x8003, 113}, + {0x8006, 113}, + {0x800a, 113}, + {0x800f, 113}, + {0x8018, 113}, + {0x801f, 113}, + {0x8029, 113}, + {0xc038, 113}, + {0x8003, 118}, + {0x8006, 118}, + {0x800a, 118}, + {0x800f, 118}, + {0x8018, 118}, + {0x801f, 118}, + {0x8029, 118}, + {0xc038, 118}, + }, + /* 64 */ + { + {0x8001, 119}, + {0xc016, 119}, + {0x8001, 120}, + {0xc016, 120}, + {0x8001, 121}, + {0xc016, 121}, + {0x8001, 122}, + {0xc016, 122}, + {0xc000, 38}, + {0xc000, 42}, + {0xc000, 44}, + {0xc000, 59}, + {0xc000, 88}, + {0xc000, 90}, + {0x4b, 0}, + {0x4e, 0}, + }, + /* 65 */ + { + {0x8002, 119}, + {0x8009, 119}, + {0x8017, 119}, + {0xc028, 119}, + {0x8002, 120}, + {0x8009, 120}, + {0x8017, 120}, + {0xc028, 120}, + {0x8002, 121}, + {0x8009, 121}, + {0x8017, 121}, + {0xc028, 121}, + {0x8002, 122}, + {0x8009, 122}, + {0x8017, 122}, + {0xc028, 122}, + }, + /* 66 */ + { + {0x8003, 119}, + {0x8006, 119}, + {0x800a, 119}, + {0x800f, 119}, + {0x8018, 119}, + {0x801f, 119}, + {0x8029, 119}, + {0xc038, 119}, + {0x8003, 120}, + {0x8006, 120}, + {0x800a, 120}, + {0x800f, 120}, + {0x8018, 120}, + {0x801f, 120}, + {0x8029, 120}, + {0xc038, 120}, + }, + /* 67 */ + { + {0x8003, 121}, + {0x8006, 121}, + {0x800a, 121}, + {0x800f, 121}, + {0x8018, 121}, + {0x801f, 121}, + {0x8029, 121}, + {0xc038, 121}, + {0x8003, 122}, + {0x8006, 122}, + {0x800a, 122}, + {0x800f, 122}, + {0x8018, 122}, + {0x801f, 122}, + {0x8029, 122}, + {0xc038, 122}, + }, + /* 68 */ + { + {0x8001, 38}, + {0xc016, 38}, + {0x8001, 42}, + {0xc016, 42}, + {0x8001, 44}, + {0xc016, 44}, + {0x8001, 59}, + {0xc016, 59}, + {0x8001, 88}, + {0xc016, 88}, + {0x8001, 90}, + {0xc016, 90}, + {0x4c, 0}, + {0x4d, 0}, + {0x4f, 0}, + {0x51, 0}, + }, + /* 69 */ + { + {0x8002, 38}, + {0x8009, 38}, + {0x8017, 38}, + {0xc028, 38}, + {0x8002, 42}, + {0x8009, 42}, + {0x8017, 42}, + {0xc028, 42}, + {0x8002, 44}, + {0x8009, 44}, + {0x8017, 44}, + {0xc028, 44}, + {0x8002, 59}, + {0x8009, 59}, + {0x8017, 59}, + {0xc028, 59}, + }, + /* 70 */ + { + {0x8003, 38}, + {0x8006, 38}, + {0x800a, 38}, + {0x800f, 38}, + {0x8018, 38}, + {0x801f, 38}, + {0x8029, 38}, + {0xc038, 38}, + {0x8003, 42}, + {0x8006, 42}, + {0x800a, 42}, + {0x800f, 42}, + {0x8018, 42}, + {0x801f, 42}, + {0x8029, 42}, + {0xc038, 42}, + }, + /* 71 */ + { + {0x8003, 44}, + {0x8006, 44}, + {0x800a, 44}, + {0x800f, 44}, + {0x8018, 44}, + {0x801f, 44}, + {0x8029, 44}, + {0xc038, 44}, + {0x8003, 59}, + {0x8006, 59}, + {0x800a, 59}, + {0x800f, 59}, + {0x8018, 59}, + {0x801f, 59}, + {0x8029, 59}, + {0xc038, 59}, + }, + /* 72 */ + { + {0x8002, 88}, + {0x8009, 88}, + {0x8017, 88}, + {0xc028, 88}, + {0x8002, 90}, + {0x8009, 90}, + {0x8017, 90}, + {0xc028, 90}, + {0xc000, 33}, + {0xc000, 34}, + {0xc000, 40}, + {0xc000, 41}, + {0xc000, 63}, + {0x50, 0}, + {0x52, 0}, + {0x54, 0}, + }, + /* 73 */ + { + {0x8003, 88}, + {0x8006, 88}, + {0x800a, 88}, + {0x800f, 88}, + {0x8018, 88}, + {0x801f, 88}, + {0x8029, 88}, + {0xc038, 88}, + {0x8003, 90}, + {0x8006, 90}, + {0x800a, 90}, + {0x800f, 90}, + {0x8018, 90}, + {0x801f, 90}, + {0x8029, 90}, + {0xc038, 90}, + }, + /* 74 */ + { + {0x8001, 33}, + {0xc016, 33}, + {0x8001, 34}, + {0xc016, 34}, + {0x8001, 40}, + {0xc016, 40}, + {0x8001, 41}, + {0xc016, 41}, + {0x8001, 63}, + {0xc016, 63}, + {0xc000, 39}, + {0xc000, 43}, + {0xc000, 124}, + {0x53, 0}, + {0x55, 0}, + {0x58, 0}, + }, + /* 75 */ + { + {0x8002, 33}, + {0x8009, 33}, + {0x8017, 33}, + {0xc028, 33}, + {0x8002, 34}, + {0x8009, 34}, + {0x8017, 34}, + {0xc028, 34}, + {0x8002, 40}, + {0x8009, 40}, + {0x8017, 40}, + {0xc028, 40}, + {0x8002, 41}, + {0x8009, 41}, + {0x8017, 41}, + {0xc028, 41}, + }, + /* 76 */ + { + {0x8003, 33}, + {0x8006, 33}, + {0x800a, 33}, + {0x800f, 33}, + {0x8018, 33}, + {0x801f, 33}, + {0x8029, 33}, + {0xc038, 33}, + {0x8003, 34}, + {0x8006, 34}, + {0x800a, 34}, + {0x800f, 34}, + {0x8018, 34}, + {0x801f, 34}, + {0x8029, 34}, + {0xc038, 34}, + }, + /* 77 */ + { + {0x8003, 40}, + {0x8006, 40}, + {0x800a, 40}, + {0x800f, 40}, + {0x8018, 40}, + {0x801f, 40}, + {0x8029, 40}, + {0xc038, 40}, + {0x8003, 41}, + {0x8006, 41}, + {0x800a, 41}, + {0x800f, 41}, + {0x8018, 41}, + {0x801f, 41}, + {0x8029, 41}, + {0xc038, 41}, + }, + /* 78 */ + { + {0x8002, 63}, + {0x8009, 63}, + {0x8017, 63}, + {0xc028, 63}, + {0x8001, 39}, + {0xc016, 39}, + {0x8001, 43}, + {0xc016, 43}, + {0x8001, 124}, + {0xc016, 124}, + {0xc000, 35}, + {0xc000, 62}, + {0x56, 0}, + {0x57, 0}, + {0x59, 0}, + {0x5a, 0}, + }, + /* 79 */ + { + {0x8003, 63}, + {0x8006, 63}, + {0x800a, 63}, + {0x800f, 63}, + {0x8018, 63}, + {0x801f, 63}, + {0x8029, 63}, + {0xc038, 63}, + {0x8002, 39}, + {0x8009, 39}, + {0x8017, 39}, + {0xc028, 39}, + {0x8002, 43}, + {0x8009, 43}, + {0x8017, 43}, + {0xc028, 43}, + }, + /* 80 */ + { + {0x8003, 39}, + {0x8006, 39}, + {0x800a, 39}, + {0x800f, 39}, + {0x8018, 39}, + {0x801f, 39}, + {0x8029, 39}, + {0xc038, 39}, + {0x8003, 43}, + {0x8006, 43}, + {0x800a, 43}, + {0x800f, 43}, + {0x8018, 43}, + {0x801f, 43}, + {0x8029, 43}, + {0xc038, 43}, + }, + /* 81 */ + { + {0x8002, 124}, + {0x8009, 124}, + {0x8017, 124}, + {0xc028, 124}, + {0x8001, 35}, + {0xc016, 35}, + {0x8001, 62}, + {0xc016, 62}, + {0xc000, 0}, + {0xc000, 36}, + {0xc000, 64}, + {0xc000, 91}, + {0xc000, 93}, + {0xc000, 126}, + {0x5b, 0}, + {0x5c, 0}, + }, + /* 82 */ + { + {0x8003, 124}, + {0x8006, 124}, + {0x800a, 124}, + {0x800f, 124}, + {0x8018, 124}, + {0x801f, 124}, + {0x8029, 124}, + {0xc038, 124}, + {0x8002, 35}, + {0x8009, 35}, + {0x8017, 35}, + {0xc028, 35}, + {0x8002, 62}, + {0x8009, 62}, + {0x8017, 62}, + {0xc028, 62}, + }, + /* 83 */ + { + {0x8003, 35}, + {0x8006, 35}, + {0x800a, 35}, + {0x800f, 35}, + {0x8018, 35}, + {0x801f, 35}, + {0x8029, 35}, + {0xc038, 35}, + {0x8003, 62}, + {0x8006, 62}, + {0x800a, 62}, + {0x800f, 62}, + {0x8018, 62}, + {0x801f, 62}, + {0x8029, 62}, + {0xc038, 62}, + }, + /* 84 */ + { + {0x8001, 0}, + {0xc016, 0}, + {0x8001, 36}, + {0xc016, 36}, + {0x8001, 64}, + {0xc016, 64}, + {0x8001, 91}, + {0xc016, 91}, + {0x8001, 93}, + {0xc016, 93}, + {0x8001, 126}, + {0xc016, 126}, + {0xc000, 94}, + {0xc000, 125}, + {0x5d, 0}, + {0x5e, 0}, + }, + /* 85 */ + { + {0x8002, 0}, + {0x8009, 0}, + {0x8017, 0}, + {0xc028, 0}, + {0x8002, 36}, + {0x8009, 36}, + {0x8017, 36}, + {0xc028, 36}, + {0x8002, 64}, + {0x8009, 64}, + {0x8017, 64}, + {0xc028, 64}, + {0x8002, 91}, + {0x8009, 91}, + {0x8017, 91}, + {0xc028, 91}, + }, + /* 86 */ + { + {0x8003, 0}, + {0x8006, 0}, + {0x800a, 0}, + {0x800f, 0}, + {0x8018, 0}, + {0x801f, 0}, + {0x8029, 0}, + {0xc038, 0}, + {0x8003, 36}, + {0x8006, 36}, + {0x800a, 36}, + {0x800f, 36}, + {0x8018, 36}, + {0x801f, 36}, + {0x8029, 36}, + {0xc038, 36}, + }, + /* 87 */ + { + {0x8003, 64}, + {0x8006, 64}, + {0x800a, 64}, + {0x800f, 64}, + {0x8018, 64}, + {0x801f, 64}, + {0x8029, 64}, + {0xc038, 64}, + {0x8003, 91}, + {0x8006, 91}, + {0x800a, 91}, + {0x800f, 91}, + {0x8018, 91}, + {0x801f, 91}, + {0x8029, 91}, + {0xc038, 91}, + }, + /* 88 */ + { + {0x8002, 93}, + {0x8009, 93}, + {0x8017, 93}, + {0xc028, 93}, + {0x8002, 126}, + {0x8009, 126}, + {0x8017, 126}, + {0xc028, 126}, + {0x8001, 94}, + {0xc016, 94}, + {0x8001, 125}, + {0xc016, 125}, + {0xc000, 60}, + {0xc000, 96}, + {0xc000, 123}, + {0x5f, 0}, + }, + /* 89 */ + { + {0x8003, 93}, + {0x8006, 93}, + {0x800a, 93}, + {0x800f, 93}, + {0x8018, 93}, + {0x801f, 93}, + {0x8029, 93}, + {0xc038, 93}, + {0x8003, 126}, + {0x8006, 126}, + {0x800a, 126}, + {0x800f, 126}, + {0x8018, 126}, + {0x801f, 126}, + {0x8029, 126}, + {0xc038, 126}, + }, + /* 90 */ + { + {0x8002, 94}, + {0x8009, 94}, + {0x8017, 94}, + {0xc028, 94}, + {0x8002, 125}, + {0x8009, 125}, + {0x8017, 125}, + {0xc028, 125}, + {0x8001, 60}, + {0xc016, 60}, + {0x8001, 96}, + {0xc016, 96}, + {0x8001, 123}, + {0xc016, 123}, + {0x60, 0}, + {0x6e, 0}, + }, + /* 91 */ + { + {0x8003, 94}, + {0x8006, 94}, + {0x800a, 94}, + {0x800f, 94}, + {0x8018, 94}, + {0x801f, 94}, + {0x8029, 94}, + {0xc038, 94}, + {0x8003, 125}, + {0x8006, 125}, + {0x800a, 125}, + {0x800f, 125}, + {0x8018, 125}, + {0x801f, 125}, + {0x8029, 125}, + {0xc038, 125}, + }, + /* 92 */ + { + {0x8002, 60}, + {0x8009, 60}, + {0x8017, 60}, + {0xc028, 60}, + {0x8002, 96}, + {0x8009, 96}, + {0x8017, 96}, + {0xc028, 96}, + {0x8002, 123}, + {0x8009, 123}, + {0x8017, 123}, + {0xc028, 123}, + {0x61, 0}, + {0x65, 0}, + {0x6f, 0}, + {0x85, 0}, + }, + /* 93 */ + { + {0x8003, 60}, + {0x8006, 60}, + {0x800a, 60}, + {0x800f, 60}, + {0x8018, 60}, + {0x801f, 60}, + {0x8029, 60}, + {0xc038, 60}, + {0x8003, 96}, + {0x8006, 96}, + {0x800a, 96}, + {0x800f, 96}, + {0x8018, 96}, + {0x801f, 96}, + {0x8029, 96}, + {0xc038, 96}, + }, + /* 94 */ + { + {0x8003, 123}, + {0x8006, 123}, + {0x800a, 123}, + {0x800f, 123}, + {0x8018, 123}, + {0x801f, 123}, + {0x8029, 123}, + {0xc038, 123}, + {0x62, 0}, + {0x63, 0}, + {0x66, 0}, + {0x69, 0}, + {0x70, 0}, + {0x77, 0}, + {0x86, 0}, + {0x99, 0}, + }, + /* 95 */ + { + {0xc000, 92}, + {0xc000, 195}, + {0xc000, 208}, + {0x64, 0}, + {0x67, 0}, + {0x68, 0}, + {0x6a, 0}, + {0x6b, 0}, + {0x71, 0}, + {0x74, 0}, + {0x78, 0}, + {0x7e, 0}, + {0x87, 0}, + {0x8e, 0}, + {0x9a, 0}, + {0xa9, 0}, + }, + /* 96 */ + { + {0x8001, 92}, + {0xc016, 92}, + {0x8001, 195}, + {0xc016, 195}, + {0x8001, 208}, + {0xc016, 208}, + {0xc000, 128}, + {0xc000, 130}, + {0xc000, 131}, + {0xc000, 162}, + {0xc000, 184}, + {0xc000, 194}, + {0xc000, 224}, + {0xc000, 226}, + {0x6c, 0}, + {0x6d, 0}, + }, + /* 97 */ + { + {0x8002, 92}, + {0x8009, 92}, + {0x8017, 92}, + {0xc028, 92}, + {0x8002, 195}, + {0x8009, 195}, + {0x8017, 195}, + {0xc028, 195}, + {0x8002, 208}, + {0x8009, 208}, + {0x8017, 208}, + {0xc028, 208}, + {0x8001, 128}, + {0xc016, 128}, + {0x8001, 130}, + {0xc016, 130}, + }, + /* 98 */ + { + {0x8003, 92}, + {0x8006, 92}, + {0x800a, 92}, + {0x800f, 92}, + {0x8018, 92}, + {0x801f, 92}, + {0x8029, 92}, + {0xc038, 92}, + {0x8003, 195}, + {0x8006, 195}, + {0x800a, 195}, + {0x800f, 195}, + {0x8018, 195}, + {0x801f, 195}, + {0x8029, 195}, + {0xc038, 195}, + }, + /* 99 */ + { + {0x8003, 208}, + {0x8006, 208}, + {0x800a, 208}, + {0x800f, 208}, + {0x8018, 208}, + {0x801f, 208}, + {0x8029, 208}, + {0xc038, 208}, + {0x8002, 128}, + {0x8009, 128}, + {0x8017, 128}, + {0xc028, 128}, + {0x8002, 130}, + {0x8009, 130}, + {0x8017, 130}, + {0xc028, 130}, + }, + /* 100 */ + { + {0x8003, 128}, + {0x8006, 128}, + {0x800a, 128}, + {0x800f, 128}, + {0x8018, 128}, + {0x801f, 128}, + {0x8029, 128}, + {0xc038, 128}, + {0x8003, 130}, + {0x8006, 130}, + {0x800a, 130}, + {0x800f, 130}, + {0x8018, 130}, + {0x801f, 130}, + {0x8029, 130}, + {0xc038, 130}, + }, + /* 101 */ + { + {0x8001, 131}, + {0xc016, 131}, + {0x8001, 162}, + {0xc016, 162}, + {0x8001, 184}, + {0xc016, 184}, + {0x8001, 194}, + {0xc016, 194}, + {0x8001, 224}, + {0xc016, 224}, + {0x8001, 226}, + {0xc016, 226}, + {0xc000, 153}, + {0xc000, 161}, + {0xc000, 167}, + {0xc000, 172}, + }, + /* 102 */ + { + {0x8002, 131}, + {0x8009, 131}, + {0x8017, 131}, + {0xc028, 131}, + {0x8002, 162}, + {0x8009, 162}, + {0x8017, 162}, + {0xc028, 162}, + {0x8002, 184}, + {0x8009, 184}, + {0x8017, 184}, + {0xc028, 184}, + {0x8002, 194}, + {0x8009, 194}, + {0x8017, 194}, + {0xc028, 194}, + }, + /* 103 */ + { + {0x8003, 131}, + {0x8006, 131}, + {0x800a, 131}, + {0x800f, 131}, + {0x8018, 131}, + {0x801f, 131}, + {0x8029, 131}, + {0xc038, 131}, + {0x8003, 162}, + {0x8006, 162}, + {0x800a, 162}, + {0x800f, 162}, + {0x8018, 162}, + {0x801f, 162}, + {0x8029, 162}, + {0xc038, 162}, + }, + /* 104 */ + { + {0x8003, 184}, + {0x8006, 184}, + {0x800a, 184}, + {0x800f, 184}, + {0x8018, 184}, + {0x801f, 184}, + {0x8029, 184}, + {0xc038, 184}, + {0x8003, 194}, + {0x8006, 194}, + {0x800a, 194}, + {0x800f, 194}, + {0x8018, 194}, + {0x801f, 194}, + {0x8029, 194}, + {0xc038, 194}, + }, + /* 105 */ + { + {0x8002, 224}, + {0x8009, 224}, + {0x8017, 224}, + {0xc028, 224}, + {0x8002, 226}, + {0x8009, 226}, + {0x8017, 226}, + {0xc028, 226}, + {0x8001, 153}, + {0xc016, 153}, + {0x8001, 161}, + {0xc016, 161}, + {0x8001, 167}, + {0xc016, 167}, + {0x8001, 172}, + {0xc016, 172}, + }, + /* 106 */ + { + {0x8003, 224}, + {0x8006, 224}, + {0x800a, 224}, + {0x800f, 224}, + {0x8018, 224}, + {0x801f, 224}, + {0x8029, 224}, + {0xc038, 224}, + {0x8003, 226}, + {0x8006, 226}, + {0x800a, 226}, + {0x800f, 226}, + {0x8018, 226}, + {0x801f, 226}, + {0x8029, 226}, + {0xc038, 226}, + }, + /* 107 */ + { + {0x8002, 153}, + {0x8009, 153}, + {0x8017, 153}, + {0xc028, 153}, + {0x8002, 161}, + {0x8009, 161}, + {0x8017, 161}, + {0xc028, 161}, + {0x8002, 167}, + {0x8009, 167}, + {0x8017, 167}, + {0xc028, 167}, + {0x8002, 172}, + {0x8009, 172}, + {0x8017, 172}, + {0xc028, 172}, + }, + /* 108 */ + { + {0x8003, 153}, + {0x8006, 153}, + {0x800a, 153}, + {0x800f, 153}, + {0x8018, 153}, + {0x801f, 153}, + {0x8029, 153}, + {0xc038, 153}, + {0x8003, 161}, + {0x8006, 161}, + {0x800a, 161}, + {0x800f, 161}, + {0x8018, 161}, + {0x801f, 161}, + {0x8029, 161}, + {0xc038, 161}, + }, + /* 109 */ + { + {0x8003, 167}, + {0x8006, 167}, + {0x800a, 167}, + {0x800f, 167}, + {0x8018, 167}, + {0x801f, 167}, + {0x8029, 167}, + {0xc038, 167}, + {0x8003, 172}, + {0x8006, 172}, + {0x800a, 172}, + {0x800f, 172}, + {0x8018, 172}, + {0x801f, 172}, + {0x8029, 172}, + {0xc038, 172}, + }, + /* 110 */ + { + {0x72, 0}, + {0x73, 0}, + {0x75, 0}, + {0x76, 0}, + {0x79, 0}, + {0x7b, 0}, + {0x7f, 0}, + {0x82, 0}, + {0x88, 0}, + {0x8b, 0}, + {0x8f, 0}, + {0x92, 0}, + {0x9b, 0}, + {0xa2, 0}, + {0xaa, 0}, + {0xb4, 0}, + }, + /* 111 */ + { + {0xc000, 176}, + {0xc000, 177}, + {0xc000, 179}, + {0xc000, 209}, + {0xc000, 216}, + {0xc000, 217}, + {0xc000, 227}, + {0xc000, 229}, + {0xc000, 230}, + {0x7a, 0}, + {0x7c, 0}, + {0x7d, 0}, + {0x80, 0}, + {0x81, 0}, + {0x83, 0}, + {0x84, 0}, + }, + /* 112 */ + { + {0x8001, 176}, + {0xc016, 176}, + {0x8001, 177}, + {0xc016, 177}, + {0x8001, 179}, + {0xc016, 179}, + {0x8001, 209}, + {0xc016, 209}, + {0x8001, 216}, + {0xc016, 216}, + {0x8001, 217}, + {0xc016, 217}, + {0x8001, 227}, + {0xc016, 227}, + {0x8001, 229}, + {0xc016, 229}, + }, + /* 113 */ + { + {0x8002, 176}, + {0x8009, 176}, + {0x8017, 176}, + {0xc028, 176}, + {0x8002, 177}, + {0x8009, 177}, + {0x8017, 177}, + {0xc028, 177}, + {0x8002, 179}, + {0x8009, 179}, + {0x8017, 179}, + {0xc028, 179}, + {0x8002, 209}, + {0x8009, 209}, + {0x8017, 209}, + {0xc028, 209}, + }, + /* 114 */ + { + {0x8003, 176}, + {0x8006, 176}, + {0x800a, 176}, + {0x800f, 176}, + {0x8018, 176}, + {0x801f, 176}, + {0x8029, 176}, + {0xc038, 176}, + {0x8003, 177}, + {0x8006, 177}, + {0x800a, 177}, + {0x800f, 177}, + {0x8018, 177}, + {0x801f, 177}, + {0x8029, 177}, + {0xc038, 177}, + }, + /* 115 */ + { + {0x8003, 179}, + {0x8006, 179}, + {0x800a, 179}, + {0x800f, 179}, + {0x8018, 179}, + {0x801f, 179}, + {0x8029, 179}, + {0xc038, 179}, + {0x8003, 209}, + {0x8006, 209}, + {0x800a, 209}, + {0x800f, 209}, + {0x8018, 209}, + {0x801f, 209}, + {0x8029, 209}, + {0xc038, 209}, + }, + /* 116 */ + { + {0x8002, 216}, + {0x8009, 216}, + {0x8017, 216}, + {0xc028, 216}, + {0x8002, 217}, + {0x8009, 217}, + {0x8017, 217}, + {0xc028, 217}, + {0x8002, 227}, + {0x8009, 227}, + {0x8017, 227}, + {0xc028, 227}, + {0x8002, 229}, + {0x8009, 229}, + {0x8017, 229}, + {0xc028, 229}, + }, + /* 117 */ + { + {0x8003, 216}, + {0x8006, 216}, + {0x800a, 216}, + {0x800f, 216}, + {0x8018, 216}, + {0x801f, 216}, + {0x8029, 216}, + {0xc038, 216}, + {0x8003, 217}, + {0x8006, 217}, + {0x800a, 217}, + {0x800f, 217}, + {0x8018, 217}, + {0x801f, 217}, + {0x8029, 217}, + {0xc038, 217}, + }, + /* 118 */ + { + {0x8003, 227}, + {0x8006, 227}, + {0x800a, 227}, + {0x800f, 227}, + {0x8018, 227}, + {0x801f, 227}, + {0x8029, 227}, + {0xc038, 227}, + {0x8003, 229}, + {0x8006, 229}, + {0x800a, 229}, + {0x800f, 229}, + {0x8018, 229}, + {0x801f, 229}, + {0x8029, 229}, + {0xc038, 229}, + }, + /* 119 */ + { + {0x8001, 230}, + {0xc016, 230}, + {0xc000, 129}, + {0xc000, 132}, + {0xc000, 133}, + {0xc000, 134}, + {0xc000, 136}, + {0xc000, 146}, + {0xc000, 154}, + {0xc000, 156}, + {0xc000, 160}, + {0xc000, 163}, + {0xc000, 164}, + {0xc000, 169}, + {0xc000, 170}, + {0xc000, 173}, + }, + /* 120 */ + { + {0x8002, 230}, + {0x8009, 230}, + {0x8017, 230}, + {0xc028, 230}, + {0x8001, 129}, + {0xc016, 129}, + {0x8001, 132}, + {0xc016, 132}, + {0x8001, 133}, + {0xc016, 133}, + {0x8001, 134}, + {0xc016, 134}, + {0x8001, 136}, + {0xc016, 136}, + {0x8001, 146}, + {0xc016, 146}, + }, + /* 121 */ + { + {0x8003, 230}, + {0x8006, 230}, + {0x800a, 230}, + {0x800f, 230}, + {0x8018, 230}, + {0x801f, 230}, + {0x8029, 230}, + {0xc038, 230}, + {0x8002, 129}, + {0x8009, 129}, + {0x8017, 129}, + {0xc028, 129}, + {0x8002, 132}, + {0x8009, 132}, + {0x8017, 132}, + {0xc028, 132}, + }, + /* 122 */ + { + {0x8003, 129}, + {0x8006, 129}, + {0x800a, 129}, + {0x800f, 129}, + {0x8018, 129}, + {0x801f, 129}, + {0x8029, 129}, + {0xc038, 129}, + {0x8003, 132}, + {0x8006, 132}, + {0x800a, 132}, + {0x800f, 132}, + {0x8018, 132}, + {0x801f, 132}, + {0x8029, 132}, + {0xc038, 132}, + }, + /* 123 */ + { + {0x8002, 133}, + {0x8009, 133}, + {0x8017, 133}, + {0xc028, 133}, + {0x8002, 134}, + {0x8009, 134}, + {0x8017, 134}, + {0xc028, 134}, + {0x8002, 136}, + {0x8009, 136}, + {0x8017, 136}, + {0xc028, 136}, + {0x8002, 146}, + {0x8009, 146}, + {0x8017, 146}, + {0xc028, 146}, + }, + /* 124 */ + { + {0x8003, 133}, + {0x8006, 133}, + {0x800a, 133}, + {0x800f, 133}, + {0x8018, 133}, + {0x801f, 133}, + {0x8029, 133}, + {0xc038, 133}, + {0x8003, 134}, + {0x8006, 134}, + {0x800a, 134}, + {0x800f, 134}, + {0x8018, 134}, + {0x801f, 134}, + {0x8029, 134}, + {0xc038, 134}, + }, + /* 125 */ + { + {0x8003, 136}, + {0x8006, 136}, + {0x800a, 136}, + {0x800f, 136}, + {0x8018, 136}, + {0x801f, 136}, + {0x8029, 136}, + {0xc038, 136}, + {0x8003, 146}, + {0x8006, 146}, + {0x800a, 146}, + {0x800f, 146}, + {0x8018, 146}, + {0x801f, 146}, + {0x8029, 146}, + {0xc038, 146}, + }, + /* 126 */ + { + {0x8001, 154}, + {0xc016, 154}, + {0x8001, 156}, + {0xc016, 156}, + {0x8001, 160}, + {0xc016, 160}, + {0x8001, 163}, + {0xc016, 163}, + {0x8001, 164}, + {0xc016, 164}, + {0x8001, 169}, + {0xc016, 169}, + {0x8001, 170}, + {0xc016, 170}, + {0x8001, 173}, + {0xc016, 173}, + }, + /* 127 */ + { + {0x8002, 154}, + {0x8009, 154}, + {0x8017, 154}, + {0xc028, 154}, + {0x8002, 156}, + {0x8009, 156}, + {0x8017, 156}, + {0xc028, 156}, + {0x8002, 160}, + {0x8009, 160}, + {0x8017, 160}, + {0xc028, 160}, + {0x8002, 163}, + {0x8009, 163}, + {0x8017, 163}, + {0xc028, 163}, + }, + /* 128 */ + { + {0x8003, 154}, + {0x8006, 154}, + {0x800a, 154}, + {0x800f, 154}, + {0x8018, 154}, + {0x801f, 154}, + {0x8029, 154}, + {0xc038, 154}, + {0x8003, 156}, + {0x8006, 156}, + {0x800a, 156}, + {0x800f, 156}, + {0x8018, 156}, + {0x801f, 156}, + {0x8029, 156}, + {0xc038, 156}, + }, + /* 129 */ + { + {0x8003, 160}, + {0x8006, 160}, + {0x800a, 160}, + {0x800f, 160}, + {0x8018, 160}, + {0x801f, 160}, + {0x8029, 160}, + {0xc038, 160}, + {0x8003, 163}, + {0x8006, 163}, + {0x800a, 163}, + {0x800f, 163}, + {0x8018, 163}, + {0x801f, 163}, + {0x8029, 163}, + {0xc038, 163}, + }, + /* 130 */ + { + {0x8002, 164}, + {0x8009, 164}, + {0x8017, 164}, + {0xc028, 164}, + {0x8002, 169}, + {0x8009, 169}, + {0x8017, 169}, + {0xc028, 169}, + {0x8002, 170}, + {0x8009, 170}, + {0x8017, 170}, + {0xc028, 170}, + {0x8002, 173}, + {0x8009, 173}, + {0x8017, 173}, + {0xc028, 173}, + }, + /* 131 */ + { + {0x8003, 164}, + {0x8006, 164}, + {0x800a, 164}, + {0x800f, 164}, + {0x8018, 164}, + {0x801f, 164}, + {0x8029, 164}, + {0xc038, 164}, + {0x8003, 169}, + {0x8006, 169}, + {0x800a, 169}, + {0x800f, 169}, + {0x8018, 169}, + {0x801f, 169}, + {0x8029, 169}, + {0xc038, 169}, + }, + /* 132 */ + { + {0x8003, 170}, + {0x8006, 170}, + {0x800a, 170}, + {0x800f, 170}, + {0x8018, 170}, + {0x801f, 170}, + {0x8029, 170}, + {0xc038, 170}, + {0x8003, 173}, + {0x8006, 173}, + {0x800a, 173}, + {0x800f, 173}, + {0x8018, 173}, + {0x801f, 173}, + {0x8029, 173}, + {0xc038, 173}, + }, + /* 133 */ + { + {0x89, 0}, + {0x8a, 0}, + {0x8c, 0}, + {0x8d, 0}, + {0x90, 0}, + {0x91, 0}, + {0x93, 0}, + {0x96, 0}, + {0x9c, 0}, + {0x9f, 0}, + {0xa3, 0}, + {0xa6, 0}, + {0xab, 0}, + {0xae, 0}, + {0xb5, 0}, + {0xbe, 0}, + }, + /* 134 */ + { + {0xc000, 178}, + {0xc000, 181}, + {0xc000, 185}, + {0xc000, 186}, + {0xc000, 187}, + {0xc000, 189}, + {0xc000, 190}, + {0xc000, 196}, + {0xc000, 198}, + {0xc000, 228}, + {0xc000, 232}, + {0xc000, 233}, + {0x94, 0}, + {0x95, 0}, + {0x97, 0}, + {0x98, 0}, + }, + /* 135 */ + { + {0x8001, 178}, + {0xc016, 178}, + {0x8001, 181}, + {0xc016, 181}, + {0x8001, 185}, + {0xc016, 185}, + {0x8001, 186}, + {0xc016, 186}, + {0x8001, 187}, + {0xc016, 187}, + {0x8001, 189}, + {0xc016, 189}, + {0x8001, 190}, + {0xc016, 190}, + {0x8001, 196}, + {0xc016, 196}, + }, + /* 136 */ + { + {0x8002, 178}, + {0x8009, 178}, + {0x8017, 178}, + {0xc028, 178}, + {0x8002, 181}, + {0x8009, 181}, + {0x8017, 181}, + {0xc028, 181}, + {0x8002, 185}, + {0x8009, 185}, + {0x8017, 185}, + {0xc028, 185}, + {0x8002, 186}, + {0x8009, 186}, + {0x8017, 186}, + {0xc028, 186}, + }, + /* 137 */ + { + {0x8003, 178}, + {0x8006, 178}, + {0x800a, 178}, + {0x800f, 178}, + {0x8018, 178}, + {0x801f, 178}, + {0x8029, 178}, + {0xc038, 178}, + {0x8003, 181}, + {0x8006, 181}, + {0x800a, 181}, + {0x800f, 181}, + {0x8018, 181}, + {0x801f, 181}, + {0x8029, 181}, + {0xc038, 181}, + }, + /* 138 */ + { + {0x8003, 185}, + {0x8006, 185}, + {0x800a, 185}, + {0x800f, 185}, + {0x8018, 185}, + {0x801f, 185}, + {0x8029, 185}, + {0xc038, 185}, + {0x8003, 186}, + {0x8006, 186}, + {0x800a, 186}, + {0x800f, 186}, + {0x8018, 186}, + {0x801f, 186}, + {0x8029, 186}, + {0xc038, 186}, + }, + /* 139 */ + { + {0x8002, 187}, + {0x8009, 187}, + {0x8017, 187}, + {0xc028, 187}, + {0x8002, 189}, + {0x8009, 189}, + {0x8017, 189}, + {0xc028, 189}, + {0x8002, 190}, + {0x8009, 190}, + {0x8017, 190}, + {0xc028, 190}, + {0x8002, 196}, + {0x8009, 196}, + {0x8017, 196}, + {0xc028, 196}, + }, + /* 140 */ + { + {0x8003, 187}, + {0x8006, 187}, + {0x800a, 187}, + {0x800f, 187}, + {0x8018, 187}, + {0x801f, 187}, + {0x8029, 187}, + {0xc038, 187}, + {0x8003, 189}, + {0x8006, 189}, + {0x800a, 189}, + {0x800f, 189}, + {0x8018, 189}, + {0x801f, 189}, + {0x8029, 189}, + {0xc038, 189}, + }, + /* 141 */ + { + {0x8003, 190}, + {0x8006, 190}, + {0x800a, 190}, + {0x800f, 190}, + {0x8018, 190}, + {0x801f, 190}, + {0x8029, 190}, + {0xc038, 190}, + {0x8003, 196}, + {0x8006, 196}, + {0x800a, 196}, + {0x800f, 196}, + {0x8018, 196}, + {0x801f, 196}, + {0x8029, 196}, + {0xc038, 196}, + }, + /* 142 */ + { + {0x8001, 198}, + {0xc016, 198}, + {0x8001, 228}, + {0xc016, 228}, + {0x8001, 232}, + {0xc016, 232}, + {0x8001, 233}, + {0xc016, 233}, + {0xc000, 1}, + {0xc000, 135}, + {0xc000, 137}, + {0xc000, 138}, + {0xc000, 139}, + {0xc000, 140}, + {0xc000, 141}, + {0xc000, 143}, + }, + /* 143 */ + { + {0x8002, 198}, + {0x8009, 198}, + {0x8017, 198}, + {0xc028, 198}, + {0x8002, 228}, + {0x8009, 228}, + {0x8017, 228}, + {0xc028, 228}, + {0x8002, 232}, + {0x8009, 232}, + {0x8017, 232}, + {0xc028, 232}, + {0x8002, 233}, + {0x8009, 233}, + {0x8017, 233}, + {0xc028, 233}, + }, + /* 144 */ + { + {0x8003, 198}, + {0x8006, 198}, + {0x800a, 198}, + {0x800f, 198}, + {0x8018, 198}, + {0x801f, 198}, + {0x8029, 198}, + {0xc038, 198}, + {0x8003, 228}, + {0x8006, 228}, + {0x800a, 228}, + {0x800f, 228}, + {0x8018, 228}, + {0x801f, 228}, + {0x8029, 228}, + {0xc038, 228}, + }, + /* 145 */ + { + {0x8003, 232}, + {0x8006, 232}, + {0x800a, 232}, + {0x800f, 232}, + {0x8018, 232}, + {0x801f, 232}, + {0x8029, 232}, + {0xc038, 232}, + {0x8003, 233}, + {0x8006, 233}, + {0x800a, 233}, + {0x800f, 233}, + {0x8018, 233}, + {0x801f, 233}, + {0x8029, 233}, + {0xc038, 233}, + }, + /* 146 */ + { + {0x8001, 1}, + {0xc016, 1}, + {0x8001, 135}, + {0xc016, 135}, + {0x8001, 137}, + {0xc016, 137}, + {0x8001, 138}, + {0xc016, 138}, + {0x8001, 139}, + {0xc016, 139}, + {0x8001, 140}, + {0xc016, 140}, + {0x8001, 141}, + {0xc016, 141}, + {0x8001, 143}, + {0xc016, 143}, + }, + /* 147 */ + { + {0x8002, 1}, + {0x8009, 1}, + {0x8017, 1}, + {0xc028, 1}, + {0x8002, 135}, + {0x8009, 135}, + {0x8017, 135}, + {0xc028, 135}, + {0x8002, 137}, + {0x8009, 137}, + {0x8017, 137}, + {0xc028, 137}, + {0x8002, 138}, + {0x8009, 138}, + {0x8017, 138}, + {0xc028, 138}, + }, + /* 148 */ + { + {0x8003, 1}, + {0x8006, 1}, + {0x800a, 1}, + {0x800f, 1}, + {0x8018, 1}, + {0x801f, 1}, + {0x8029, 1}, + {0xc038, 1}, + {0x8003, 135}, + {0x8006, 135}, + {0x800a, 135}, + {0x800f, 135}, + {0x8018, 135}, + {0x801f, 135}, + {0x8029, 135}, + {0xc038, 135}, + }, + /* 149 */ + { + {0x8003, 137}, + {0x8006, 137}, + {0x800a, 137}, + {0x800f, 137}, + {0x8018, 137}, + {0x801f, 137}, + {0x8029, 137}, + {0xc038, 137}, + {0x8003, 138}, + {0x8006, 138}, + {0x800a, 138}, + {0x800f, 138}, + {0x8018, 138}, + {0x801f, 138}, + {0x8029, 138}, + {0xc038, 138}, + }, + /* 150 */ + { + {0x8002, 139}, + {0x8009, 139}, + {0x8017, 139}, + {0xc028, 139}, + {0x8002, 140}, + {0x8009, 140}, + {0x8017, 140}, + {0xc028, 140}, + {0x8002, 141}, + {0x8009, 141}, + {0x8017, 141}, + {0xc028, 141}, + {0x8002, 143}, + {0x8009, 143}, + {0x8017, 143}, + {0xc028, 143}, + }, + /* 151 */ + { + {0x8003, 139}, + {0x8006, 139}, + {0x800a, 139}, + {0x800f, 139}, + {0x8018, 139}, + {0x801f, 139}, + {0x8029, 139}, + {0xc038, 139}, + {0x8003, 140}, + {0x8006, 140}, + {0x800a, 140}, + {0x800f, 140}, + {0x8018, 140}, + {0x801f, 140}, + {0x8029, 140}, + {0xc038, 140}, + }, + /* 152 */ + { + {0x8003, 141}, + {0x8006, 141}, + {0x800a, 141}, + {0x800f, 141}, + {0x8018, 141}, + {0x801f, 141}, + {0x8029, 141}, + {0xc038, 141}, + {0x8003, 143}, + {0x8006, 143}, + {0x800a, 143}, + {0x800f, 143}, + {0x8018, 143}, + {0x801f, 143}, + {0x8029, 143}, + {0xc038, 143}, + }, + /* 153 */ + { + {0x9d, 0}, + {0x9e, 0}, + {0xa0, 0}, + {0xa1, 0}, + {0xa4, 0}, + {0xa5, 0}, + {0xa7, 0}, + {0xa8, 0}, + {0xac, 0}, + {0xad, 0}, + {0xaf, 0}, + {0xb1, 0}, + {0xb6, 0}, + {0xb9, 0}, + {0xbf, 0}, + {0xcf, 0}, + }, + /* 154 */ + { + {0xc000, 147}, + {0xc000, 149}, + {0xc000, 150}, + {0xc000, 151}, + {0xc000, 152}, + {0xc000, 155}, + {0xc000, 157}, + {0xc000, 158}, + {0xc000, 165}, + {0xc000, 166}, + {0xc000, 168}, + {0xc000, 174}, + {0xc000, 175}, + {0xc000, 180}, + {0xc000, 182}, + {0xc000, 183}, + }, + /* 155 */ + { + {0x8001, 147}, + {0xc016, 147}, + {0x8001, 149}, + {0xc016, 149}, + {0x8001, 150}, + {0xc016, 150}, + {0x8001, 151}, + {0xc016, 151}, + {0x8001, 152}, + {0xc016, 152}, + {0x8001, 155}, + {0xc016, 155}, + {0x8001, 157}, + {0xc016, 157}, + {0x8001, 158}, + {0xc016, 158}, + }, + /* 156 */ + { + {0x8002, 147}, + {0x8009, 147}, + {0x8017, 147}, + {0xc028, 147}, + {0x8002, 149}, + {0x8009, 149}, + {0x8017, 149}, + {0xc028, 149}, + {0x8002, 150}, + {0x8009, 150}, + {0x8017, 150}, + {0xc028, 150}, + {0x8002, 151}, + {0x8009, 151}, + {0x8017, 151}, + {0xc028, 151}, + }, + /* 157 */ + { + {0x8003, 147}, + {0x8006, 147}, + {0x800a, 147}, + {0x800f, 147}, + {0x8018, 147}, + {0x801f, 147}, + {0x8029, 147}, + {0xc038, 147}, + {0x8003, 149}, + {0x8006, 149}, + {0x800a, 149}, + {0x800f, 149}, + {0x8018, 149}, + {0x801f, 149}, + {0x8029, 149}, + {0xc038, 149}, + }, + /* 158 */ + { + {0x8003, 150}, + {0x8006, 150}, + {0x800a, 150}, + {0x800f, 150}, + {0x8018, 150}, + {0x801f, 150}, + {0x8029, 150}, + {0xc038, 150}, + {0x8003, 151}, + {0x8006, 151}, + {0x800a, 151}, + {0x800f, 151}, + {0x8018, 151}, + {0x801f, 151}, + {0x8029, 151}, + {0xc038, 151}, + }, + /* 159 */ + { + {0x8002, 152}, + {0x8009, 152}, + {0x8017, 152}, + {0xc028, 152}, + {0x8002, 155}, + {0x8009, 155}, + {0x8017, 155}, + {0xc028, 155}, + {0x8002, 157}, + {0x8009, 157}, + {0x8017, 157}, + {0xc028, 157}, + {0x8002, 158}, + {0x8009, 158}, + {0x8017, 158}, + {0xc028, 158}, + }, + /* 160 */ + { + {0x8003, 152}, + {0x8006, 152}, + {0x800a, 152}, + {0x800f, 152}, + {0x8018, 152}, + {0x801f, 152}, + {0x8029, 152}, + {0xc038, 152}, + {0x8003, 155}, + {0x8006, 155}, + {0x800a, 155}, + {0x800f, 155}, + {0x8018, 155}, + {0x801f, 155}, + {0x8029, 155}, + {0xc038, 155}, + }, + /* 161 */ + { + {0x8003, 157}, + {0x8006, 157}, + {0x800a, 157}, + {0x800f, 157}, + {0x8018, 157}, + {0x801f, 157}, + {0x8029, 157}, + {0xc038, 157}, + {0x8003, 158}, + {0x8006, 158}, + {0x800a, 158}, + {0x800f, 158}, + {0x8018, 158}, + {0x801f, 158}, + {0x8029, 158}, + {0xc038, 158}, + }, + /* 162 */ + { + {0x8001, 165}, + {0xc016, 165}, + {0x8001, 166}, + {0xc016, 166}, + {0x8001, 168}, + {0xc016, 168}, + {0x8001, 174}, + {0xc016, 174}, + {0x8001, 175}, + {0xc016, 175}, + {0x8001, 180}, + {0xc016, 180}, + {0x8001, 182}, + {0xc016, 182}, + {0x8001, 183}, + {0xc016, 183}, + }, + /* 163 */ + { + {0x8002, 165}, + {0x8009, 165}, + {0x8017, 165}, + {0xc028, 165}, + {0x8002, 166}, + {0x8009, 166}, + {0x8017, 166}, + {0xc028, 166}, + {0x8002, 168}, + {0x8009, 168}, + {0x8017, 168}, + {0xc028, 168}, + {0x8002, 174}, + {0x8009, 174}, + {0x8017, 174}, + {0xc028, 174}, + }, + /* 164 */ + { + {0x8003, 165}, + {0x8006, 165}, + {0x800a, 165}, + {0x800f, 165}, + {0x8018, 165}, + {0x801f, 165}, + {0x8029, 165}, + {0xc038, 165}, + {0x8003, 166}, + {0x8006, 166}, + {0x800a, 166}, + {0x800f, 166}, + {0x8018, 166}, + {0x801f, 166}, + {0x8029, 166}, + {0xc038, 166}, + }, + /* 165 */ + { + {0x8003, 168}, + {0x8006, 168}, + {0x800a, 168}, + {0x800f, 168}, + {0x8018, 168}, + {0x801f, 168}, + {0x8029, 168}, + {0xc038, 168}, + {0x8003, 174}, + {0x8006, 174}, + {0x800a, 174}, + {0x800f, 174}, + {0x8018, 174}, + {0x801f, 174}, + {0x8029, 174}, + {0xc038, 174}, + }, + /* 166 */ + { + {0x8002, 175}, + {0x8009, 175}, + {0x8017, 175}, + {0xc028, 175}, + {0x8002, 180}, + {0x8009, 180}, + {0x8017, 180}, + {0xc028, 180}, + {0x8002, 182}, + {0x8009, 182}, + {0x8017, 182}, + {0xc028, 182}, + {0x8002, 183}, + {0x8009, 183}, + {0x8017, 183}, + {0xc028, 183}, + }, + /* 167 */ + { + {0x8003, 175}, + {0x8006, 175}, + {0x800a, 175}, + {0x800f, 175}, + {0x8018, 175}, + {0x801f, 175}, + {0x8029, 175}, + {0xc038, 175}, + {0x8003, 180}, + {0x8006, 180}, + {0x800a, 180}, + {0x800f, 180}, + {0x8018, 180}, + {0x801f, 180}, + {0x8029, 180}, + {0xc038, 180}, + }, + /* 168 */ + { + {0x8003, 182}, + {0x8006, 182}, + {0x800a, 182}, + {0x800f, 182}, + {0x8018, 182}, + {0x801f, 182}, + {0x8029, 182}, + {0xc038, 182}, + {0x8003, 183}, + {0x8006, 183}, + {0x800a, 183}, + {0x800f, 183}, + {0x8018, 183}, + {0x801f, 183}, + {0x8029, 183}, + {0xc038, 183}, + }, + /* 169 */ + { + {0xc000, 188}, + {0xc000, 191}, + {0xc000, 197}, + {0xc000, 231}, + {0xc000, 239}, + {0xb0, 0}, + {0xb2, 0}, + {0xb3, 0}, + {0xb7, 0}, + {0xb8, 0}, + {0xba, 0}, + {0xbb, 0}, + {0xc0, 0}, + {0xc7, 0}, + {0xd0, 0}, + {0xdf, 0}, + }, + /* 170 */ + { + {0x8001, 188}, + {0xc016, 188}, + {0x8001, 191}, + {0xc016, 191}, + {0x8001, 197}, + {0xc016, 197}, + {0x8001, 231}, + {0xc016, 231}, + {0x8001, 239}, + {0xc016, 239}, + {0xc000, 9}, + {0xc000, 142}, + {0xc000, 144}, + {0xc000, 145}, + {0xc000, 148}, + {0xc000, 159}, + }, + /* 171 */ + { + {0x8002, 188}, + {0x8009, 188}, + {0x8017, 188}, + {0xc028, 188}, + {0x8002, 191}, + {0x8009, 191}, + {0x8017, 191}, + {0xc028, 191}, + {0x8002, 197}, + {0x8009, 197}, + {0x8017, 197}, + {0xc028, 197}, + {0x8002, 231}, + {0x8009, 231}, + {0x8017, 231}, + {0xc028, 231}, + }, + /* 172 */ + { + {0x8003, 188}, + {0x8006, 188}, + {0x800a, 188}, + {0x800f, 188}, + {0x8018, 188}, + {0x801f, 188}, + {0x8029, 188}, + {0xc038, 188}, + {0x8003, 191}, + {0x8006, 191}, + {0x800a, 191}, + {0x800f, 191}, + {0x8018, 191}, + {0x801f, 191}, + {0x8029, 191}, + {0xc038, 191}, + }, + /* 173 */ + { + {0x8003, 197}, + {0x8006, 197}, + {0x800a, 197}, + {0x800f, 197}, + {0x8018, 197}, + {0x801f, 197}, + {0x8029, 197}, + {0xc038, 197}, + {0x8003, 231}, + {0x8006, 231}, + {0x800a, 231}, + {0x800f, 231}, + {0x8018, 231}, + {0x801f, 231}, + {0x8029, 231}, + {0xc038, 231}, + }, + /* 174 */ + { + {0x8002, 239}, + {0x8009, 239}, + {0x8017, 239}, + {0xc028, 239}, + {0x8001, 9}, + {0xc016, 9}, + {0x8001, 142}, + {0xc016, 142}, + {0x8001, 144}, + {0xc016, 144}, + {0x8001, 145}, + {0xc016, 145}, + {0x8001, 148}, + {0xc016, 148}, + {0x8001, 159}, + {0xc016, 159}, + }, + /* 175 */ + { + {0x8003, 239}, + {0x8006, 239}, + {0x800a, 239}, + {0x800f, 239}, + {0x8018, 239}, + {0x801f, 239}, + {0x8029, 239}, + {0xc038, 239}, + {0x8002, 9}, + {0x8009, 9}, + {0x8017, 9}, + {0xc028, 9}, + {0x8002, 142}, + {0x8009, 142}, + {0x8017, 142}, + {0xc028, 142}, + }, + /* 176 */ + { + {0x8003, 9}, + {0x8006, 9}, + {0x800a, 9}, + {0x800f, 9}, + {0x8018, 9}, + {0x801f, 9}, + {0x8029, 9}, + {0xc038, 9}, + {0x8003, 142}, + {0x8006, 142}, + {0x800a, 142}, + {0x800f, 142}, + {0x8018, 142}, + {0x801f, 142}, + {0x8029, 142}, + {0xc038, 142}, + }, + /* 177 */ + { + {0x8002, 144}, + {0x8009, 144}, + {0x8017, 144}, + {0xc028, 144}, + {0x8002, 145}, + {0x8009, 145}, + {0x8017, 145}, + {0xc028, 145}, + {0x8002, 148}, + {0x8009, 148}, + {0x8017, 148}, + {0xc028, 148}, + {0x8002, 159}, + {0x8009, 159}, + {0x8017, 159}, + {0xc028, 159}, + }, + /* 178 */ + { + {0x8003, 144}, + {0x8006, 144}, + {0x800a, 144}, + {0x800f, 144}, + {0x8018, 144}, + {0x801f, 144}, + {0x8029, 144}, + {0xc038, 144}, + {0x8003, 145}, + {0x8006, 145}, + {0x800a, 145}, + {0x800f, 145}, + {0x8018, 145}, + {0x801f, 145}, + {0x8029, 145}, + {0xc038, 145}, + }, + /* 179 */ + { + {0x8003, 148}, + {0x8006, 148}, + {0x800a, 148}, + {0x800f, 148}, + {0x8018, 148}, + {0x801f, 148}, + {0x8029, 148}, + {0xc038, 148}, + {0x8003, 159}, + {0x8006, 159}, + {0x800a, 159}, + {0x800f, 159}, + {0x8018, 159}, + {0x801f, 159}, + {0x8029, 159}, + {0xc038, 159}, + }, + /* 180 */ + { + {0xc000, 171}, + {0xc000, 206}, + {0xc000, 215}, + {0xc000, 225}, + {0xc000, 236}, + {0xc000, 237}, + {0xbc, 0}, + {0xbd, 0}, + {0xc1, 0}, + {0xc4, 0}, + {0xc8, 0}, + {0xcb, 0}, + {0xd1, 0}, + {0xd8, 0}, + {0xe0, 0}, + {0xee, 0}, + }, + /* 181 */ + { + {0x8001, 171}, + {0xc016, 171}, + {0x8001, 206}, + {0xc016, 206}, + {0x8001, 215}, + {0xc016, 215}, + {0x8001, 225}, + {0xc016, 225}, + {0x8001, 236}, + {0xc016, 236}, + {0x8001, 237}, + {0xc016, 237}, + {0xc000, 199}, + {0xc000, 207}, + {0xc000, 234}, + {0xc000, 235}, + }, + /* 182 */ + { + {0x8002, 171}, + {0x8009, 171}, + {0x8017, 171}, + {0xc028, 171}, + {0x8002, 206}, + {0x8009, 206}, + {0x8017, 206}, + {0xc028, 206}, + {0x8002, 215}, + {0x8009, 215}, + {0x8017, 215}, + {0xc028, 215}, + {0x8002, 225}, + {0x8009, 225}, + {0x8017, 225}, + {0xc028, 225}, + }, + /* 183 */ + { + {0x8003, 171}, + {0x8006, 171}, + {0x800a, 171}, + {0x800f, 171}, + {0x8018, 171}, + {0x801f, 171}, + {0x8029, 171}, + {0xc038, 171}, + {0x8003, 206}, + {0x8006, 206}, + {0x800a, 206}, + {0x800f, 206}, + {0x8018, 206}, + {0x801f, 206}, + {0x8029, 206}, + {0xc038, 206}, + }, + /* 184 */ + { + {0x8003, 215}, + {0x8006, 215}, + {0x800a, 215}, + {0x800f, 215}, + {0x8018, 215}, + {0x801f, 215}, + {0x8029, 215}, + {0xc038, 215}, + {0x8003, 225}, + {0x8006, 225}, + {0x800a, 225}, + {0x800f, 225}, + {0x8018, 225}, + {0x801f, 225}, + {0x8029, 225}, + {0xc038, 225}, + }, + /* 185 */ + { + {0x8002, 236}, + {0x8009, 236}, + {0x8017, 236}, + {0xc028, 236}, + {0x8002, 237}, + {0x8009, 237}, + {0x8017, 237}, + {0xc028, 237}, + {0x8001, 199}, + {0xc016, 199}, + {0x8001, 207}, + {0xc016, 207}, + {0x8001, 234}, + {0xc016, 234}, + {0x8001, 235}, + {0xc016, 235}, + }, + /* 186 */ + { + {0x8003, 236}, + {0x8006, 236}, + {0x800a, 236}, + {0x800f, 236}, + {0x8018, 236}, + {0x801f, 236}, + {0x8029, 236}, + {0xc038, 236}, + {0x8003, 237}, + {0x8006, 237}, + {0x800a, 237}, + {0x800f, 237}, + {0x8018, 237}, + {0x801f, 237}, + {0x8029, 237}, + {0xc038, 237}, + }, + /* 187 */ + { + {0x8002, 199}, + {0x8009, 199}, + {0x8017, 199}, + {0xc028, 199}, + {0x8002, 207}, + {0x8009, 207}, + {0x8017, 207}, + {0xc028, 207}, + {0x8002, 234}, + {0x8009, 234}, + {0x8017, 234}, + {0xc028, 234}, + {0x8002, 235}, + {0x8009, 235}, + {0x8017, 235}, + {0xc028, 235}, + }, + /* 188 */ + { + {0x8003, 199}, + {0x8006, 199}, + {0x800a, 199}, + {0x800f, 199}, + {0x8018, 199}, + {0x801f, 199}, + {0x8029, 199}, + {0xc038, 199}, + {0x8003, 207}, + {0x8006, 207}, + {0x800a, 207}, + {0x800f, 207}, + {0x8018, 207}, + {0x801f, 207}, + {0x8029, 207}, + {0xc038, 207}, + }, + /* 189 */ + { + {0x8003, 234}, + {0x8006, 234}, + {0x800a, 234}, + {0x800f, 234}, + {0x8018, 234}, + {0x801f, 234}, + {0x8029, 234}, + {0xc038, 234}, + {0x8003, 235}, + {0x8006, 235}, + {0x800a, 235}, + {0x800f, 235}, + {0x8018, 235}, + {0x801f, 235}, + {0x8029, 235}, + {0xc038, 235}, + }, + /* 190 */ + { + {0xc2, 0}, + {0xc3, 0}, + {0xc5, 0}, + {0xc6, 0}, + {0xc9, 0}, + {0xca, 0}, + {0xcc, 0}, + {0xcd, 0}, + {0xd2, 0}, + {0xd5, 0}, + {0xd9, 0}, + {0xdc, 0}, + {0xe1, 0}, + {0xe7, 0}, + {0xef, 0}, + {0xf6, 0}, + }, + /* 191 */ + { + {0xc000, 192}, + {0xc000, 193}, + {0xc000, 200}, + {0xc000, 201}, + {0xc000, 202}, + {0xc000, 205}, + {0xc000, 210}, + {0xc000, 213}, + {0xc000, 218}, + {0xc000, 219}, + {0xc000, 238}, + {0xc000, 240}, + {0xc000, 242}, + {0xc000, 243}, + {0xc000, 255}, + {0xce, 0}, + }, + /* 192 */ + { + {0x8001, 192}, + {0xc016, 192}, + {0x8001, 193}, + {0xc016, 193}, + {0x8001, 200}, + {0xc016, 200}, + {0x8001, 201}, + {0xc016, 201}, + {0x8001, 202}, + {0xc016, 202}, + {0x8001, 205}, + {0xc016, 205}, + {0x8001, 210}, + {0xc016, 210}, + {0x8001, 213}, + {0xc016, 213}, + }, + /* 193 */ + { + {0x8002, 192}, + {0x8009, 192}, + {0x8017, 192}, + {0xc028, 192}, + {0x8002, 193}, + {0x8009, 193}, + {0x8017, 193}, + {0xc028, 193}, + {0x8002, 200}, + {0x8009, 200}, + {0x8017, 200}, + {0xc028, 200}, + {0x8002, 201}, + {0x8009, 201}, + {0x8017, 201}, + {0xc028, 201}, + }, + /* 194 */ + { + {0x8003, 192}, + {0x8006, 192}, + {0x800a, 192}, + {0x800f, 192}, + {0x8018, 192}, + {0x801f, 192}, + {0x8029, 192}, + {0xc038, 192}, + {0x8003, 193}, + {0x8006, 193}, + {0x800a, 193}, + {0x800f, 193}, + {0x8018, 193}, + {0x801f, 193}, + {0x8029, 193}, + {0xc038, 193}, + }, + /* 195 */ + { + {0x8003, 200}, + {0x8006, 200}, + {0x800a, 200}, + {0x800f, 200}, + {0x8018, 200}, + {0x801f, 200}, + {0x8029, 200}, + {0xc038, 200}, + {0x8003, 201}, + {0x8006, 201}, + {0x800a, 201}, + {0x800f, 201}, + {0x8018, 201}, + {0x801f, 201}, + {0x8029, 201}, + {0xc038, 201}, + }, + /* 196 */ + { + {0x8002, 202}, + {0x8009, 202}, + {0x8017, 202}, + {0xc028, 202}, + {0x8002, 205}, + {0x8009, 205}, + {0x8017, 205}, + {0xc028, 205}, + {0x8002, 210}, + {0x8009, 210}, + {0x8017, 210}, + {0xc028, 210}, + {0x8002, 213}, + {0x8009, 213}, + {0x8017, 213}, + {0xc028, 213}, + }, + /* 197 */ + { + {0x8003, 202}, + {0x8006, 202}, + {0x800a, 202}, + {0x800f, 202}, + {0x8018, 202}, + {0x801f, 202}, + {0x8029, 202}, + {0xc038, 202}, + {0x8003, 205}, + {0x8006, 205}, + {0x800a, 205}, + {0x800f, 205}, + {0x8018, 205}, + {0x801f, 205}, + {0x8029, 205}, + {0xc038, 205}, + }, + /* 198 */ + { + {0x8003, 210}, + {0x8006, 210}, + {0x800a, 210}, + {0x800f, 210}, + {0x8018, 210}, + {0x801f, 210}, + {0x8029, 210}, + {0xc038, 210}, + {0x8003, 213}, + {0x8006, 213}, + {0x800a, 213}, + {0x800f, 213}, + {0x8018, 213}, + {0x801f, 213}, + {0x8029, 213}, + {0xc038, 213}, + }, + /* 199 */ + { + {0x8001, 218}, + {0xc016, 218}, + {0x8001, 219}, + {0xc016, 219}, + {0x8001, 238}, + {0xc016, 238}, + {0x8001, 240}, + {0xc016, 240}, + {0x8001, 242}, + {0xc016, 242}, + {0x8001, 243}, + {0xc016, 243}, + {0x8001, 255}, + {0xc016, 255}, + {0xc000, 203}, + {0xc000, 204}, + }, + /* 200 */ + { + {0x8002, 218}, + {0x8009, 218}, + {0x8017, 218}, + {0xc028, 218}, + {0x8002, 219}, + {0x8009, 219}, + {0x8017, 219}, + {0xc028, 219}, + {0x8002, 238}, + {0x8009, 238}, + {0x8017, 238}, + {0xc028, 238}, + {0x8002, 240}, + {0x8009, 240}, + {0x8017, 240}, + {0xc028, 240}, + }, + /* 201 */ + { + {0x8003, 218}, + {0x8006, 218}, + {0x800a, 218}, + {0x800f, 218}, + {0x8018, 218}, + {0x801f, 218}, + {0x8029, 218}, + {0xc038, 218}, + {0x8003, 219}, + {0x8006, 219}, + {0x800a, 219}, + {0x800f, 219}, + {0x8018, 219}, + {0x801f, 219}, + {0x8029, 219}, + {0xc038, 219}, + }, + /* 202 */ + { + {0x8003, 238}, + {0x8006, 238}, + {0x800a, 238}, + {0x800f, 238}, + {0x8018, 238}, + {0x801f, 238}, + {0x8029, 238}, + {0xc038, 238}, + {0x8003, 240}, + {0x8006, 240}, + {0x800a, 240}, + {0x800f, 240}, + {0x8018, 240}, + {0x801f, 240}, + {0x8029, 240}, + {0xc038, 240}, + }, + /* 203 */ + { + {0x8002, 242}, + {0x8009, 242}, + {0x8017, 242}, + {0xc028, 242}, + {0x8002, 243}, + {0x8009, 243}, + {0x8017, 243}, + {0xc028, 243}, + {0x8002, 255}, + {0x8009, 255}, + {0x8017, 255}, + {0xc028, 255}, + {0x8001, 203}, + {0xc016, 203}, + {0x8001, 204}, + {0xc016, 204}, + }, + /* 204 */ + { + {0x8003, 242}, + {0x8006, 242}, + {0x800a, 242}, + {0x800f, 242}, + {0x8018, 242}, + {0x801f, 242}, + {0x8029, 242}, + {0xc038, 242}, + {0x8003, 243}, + {0x8006, 243}, + {0x800a, 243}, + {0x800f, 243}, + {0x8018, 243}, + {0x801f, 243}, + {0x8029, 243}, + {0xc038, 243}, + }, + /* 205 */ + { + {0x8003, 255}, + {0x8006, 255}, + {0x800a, 255}, + {0x800f, 255}, + {0x8018, 255}, + {0x801f, 255}, + {0x8029, 255}, + {0xc038, 255}, + {0x8002, 203}, + {0x8009, 203}, + {0x8017, 203}, + {0xc028, 203}, + {0x8002, 204}, + {0x8009, 204}, + {0x8017, 204}, + {0xc028, 204}, + }, + /* 206 */ + { + {0x8003, 203}, + {0x8006, 203}, + {0x800a, 203}, + {0x800f, 203}, + {0x8018, 203}, + {0x801f, 203}, + {0x8029, 203}, + {0xc038, 203}, + {0x8003, 204}, + {0x8006, 204}, + {0x800a, 204}, + {0x800f, 204}, + {0x8018, 204}, + {0x801f, 204}, + {0x8029, 204}, + {0xc038, 204}, + }, + /* 207 */ + { + {0xd3, 0}, + {0xd4, 0}, + {0xd6, 0}, + {0xd7, 0}, + {0xda, 0}, + {0xdb, 0}, + {0xdd, 0}, + {0xde, 0}, + {0xe2, 0}, + {0xe4, 0}, + {0xe8, 0}, + {0xeb, 0}, + {0xf0, 0}, + {0xf3, 0}, + {0xf7, 0}, + {0xfa, 0}, + }, + /* 208 */ + { + {0xc000, 211}, + {0xc000, 212}, + {0xc000, 214}, + {0xc000, 221}, + {0xc000, 222}, + {0xc000, 223}, + {0xc000, 241}, + {0xc000, 244}, + {0xc000, 245}, + {0xc000, 246}, + {0xc000, 247}, + {0xc000, 248}, + {0xc000, 250}, + {0xc000, 251}, + {0xc000, 252}, + {0xc000, 253}, + }, + /* 209 */ + { + {0x8001, 211}, + {0xc016, 211}, + {0x8001, 212}, + {0xc016, 212}, + {0x8001, 214}, + {0xc016, 214}, + {0x8001, 221}, + {0xc016, 221}, + {0x8001, 222}, + {0xc016, 222}, + {0x8001, 223}, + {0xc016, 223}, + {0x8001, 241}, + {0xc016, 241}, + {0x8001, 244}, + {0xc016, 244}, + }, + /* 210 */ + { + {0x8002, 211}, + {0x8009, 211}, + {0x8017, 211}, + {0xc028, 211}, + {0x8002, 212}, + {0x8009, 212}, + {0x8017, 212}, + {0xc028, 212}, + {0x8002, 214}, + {0x8009, 214}, + {0x8017, 214}, + {0xc028, 214}, + {0x8002, 221}, + {0x8009, 221}, + {0x8017, 221}, + {0xc028, 221}, + }, + /* 211 */ + { + {0x8003, 211}, + {0x8006, 211}, + {0x800a, 211}, + {0x800f, 211}, + {0x8018, 211}, + {0x801f, 211}, + {0x8029, 211}, + {0xc038, 211}, + {0x8003, 212}, + {0x8006, 212}, + {0x800a, 212}, + {0x800f, 212}, + {0x8018, 212}, + {0x801f, 212}, + {0x8029, 212}, + {0xc038, 212}, + }, + /* 212 */ + { + {0x8003, 214}, + {0x8006, 214}, + {0x800a, 214}, + {0x800f, 214}, + {0x8018, 214}, + {0x801f, 214}, + {0x8029, 214}, + {0xc038, 214}, + {0x8003, 221}, + {0x8006, 221}, + {0x800a, 221}, + {0x800f, 221}, + {0x8018, 221}, + {0x801f, 221}, + {0x8029, 221}, + {0xc038, 221}, + }, + /* 213 */ + { + {0x8002, 222}, + {0x8009, 222}, + {0x8017, 222}, + {0xc028, 222}, + {0x8002, 223}, + {0x8009, 223}, + {0x8017, 223}, + {0xc028, 223}, + {0x8002, 241}, + {0x8009, 241}, + {0x8017, 241}, + {0xc028, 241}, + {0x8002, 244}, + {0x8009, 244}, + {0x8017, 244}, + {0xc028, 244}, + }, + /* 214 */ + { + {0x8003, 222}, + {0x8006, 222}, + {0x800a, 222}, + {0x800f, 222}, + {0x8018, 222}, + {0x801f, 222}, + {0x8029, 222}, + {0xc038, 222}, + {0x8003, 223}, + {0x8006, 223}, + {0x800a, 223}, + {0x800f, 223}, + {0x8018, 223}, + {0x801f, 223}, + {0x8029, 223}, + {0xc038, 223}, + }, + /* 215 */ + { + {0x8003, 241}, + {0x8006, 241}, + {0x800a, 241}, + {0x800f, 241}, + {0x8018, 241}, + {0x801f, 241}, + {0x8029, 241}, + {0xc038, 241}, + {0x8003, 244}, + {0x8006, 244}, + {0x800a, 244}, + {0x800f, 244}, + {0x8018, 244}, + {0x801f, 244}, + {0x8029, 244}, + {0xc038, 244}, + }, + /* 216 */ + { + {0x8001, 245}, + {0xc016, 245}, + {0x8001, 246}, + {0xc016, 246}, + {0x8001, 247}, + {0xc016, 247}, + {0x8001, 248}, + {0xc016, 248}, + {0x8001, 250}, + {0xc016, 250}, + {0x8001, 251}, + {0xc016, 251}, + {0x8001, 252}, + {0xc016, 252}, + {0x8001, 253}, + {0xc016, 253}, + }, + /* 217 */ + { + {0x8002, 245}, + {0x8009, 245}, + {0x8017, 245}, + {0xc028, 245}, + {0x8002, 246}, + {0x8009, 246}, + {0x8017, 246}, + {0xc028, 246}, + {0x8002, 247}, + {0x8009, 247}, + {0x8017, 247}, + {0xc028, 247}, + {0x8002, 248}, + {0x8009, 248}, + {0x8017, 248}, + {0xc028, 248}, + }, + /* 218 */ + { + {0x8003, 245}, + {0x8006, 245}, + {0x800a, 245}, + {0x800f, 245}, + {0x8018, 245}, + {0x801f, 245}, + {0x8029, 245}, + {0xc038, 245}, + {0x8003, 246}, + {0x8006, 246}, + {0x800a, 246}, + {0x800f, 246}, + {0x8018, 246}, + {0x801f, 246}, + {0x8029, 246}, + {0xc038, 246}, + }, + /* 219 */ + { + {0x8003, 247}, + {0x8006, 247}, + {0x800a, 247}, + {0x800f, 247}, + {0x8018, 247}, + {0x801f, 247}, + {0x8029, 247}, + {0xc038, 247}, + {0x8003, 248}, + {0x8006, 248}, + {0x800a, 248}, + {0x800f, 248}, + {0x8018, 248}, + {0x801f, 248}, + {0x8029, 248}, + {0xc038, 248}, + }, + /* 220 */ + { + {0x8002, 250}, + {0x8009, 250}, + {0x8017, 250}, + {0xc028, 250}, + {0x8002, 251}, + {0x8009, 251}, + {0x8017, 251}, + {0xc028, 251}, + {0x8002, 252}, + {0x8009, 252}, + {0x8017, 252}, + {0xc028, 252}, + {0x8002, 253}, + {0x8009, 253}, + {0x8017, 253}, + {0xc028, 253}, + }, + /* 221 */ + { + {0x8003, 250}, + {0x8006, 250}, + {0x800a, 250}, + {0x800f, 250}, + {0x8018, 250}, + {0x801f, 250}, + {0x8029, 250}, + {0xc038, 250}, + {0x8003, 251}, + {0x8006, 251}, + {0x800a, 251}, + {0x800f, 251}, + {0x8018, 251}, + {0x801f, 251}, + {0x8029, 251}, + {0xc038, 251}, + }, + /* 222 */ + { + {0x8003, 252}, + {0x8006, 252}, + {0x800a, 252}, + {0x800f, 252}, + {0x8018, 252}, + {0x801f, 252}, + {0x8029, 252}, + {0xc038, 252}, + {0x8003, 253}, + {0x8006, 253}, + {0x800a, 253}, + {0x800f, 253}, + {0x8018, 253}, + {0x801f, 253}, + {0x8029, 253}, + {0xc038, 253}, + }, + /* 223 */ + { + {0xc000, 254}, + {0xe3, 0}, + {0xe5, 0}, + {0xe6, 0}, + {0xe9, 0}, + {0xea, 0}, + {0xec, 0}, + {0xed, 0}, + {0xf1, 0}, + {0xf2, 0}, + {0xf4, 0}, + {0xf5, 0}, + {0xf8, 0}, + {0xf9, 0}, + {0xfb, 0}, + {0xfc, 0}, + }, + /* 224 */ + { + {0x8001, 254}, + {0xc016, 254}, + {0xc000, 2}, + {0xc000, 3}, + {0xc000, 4}, + {0xc000, 5}, + {0xc000, 6}, + {0xc000, 7}, + {0xc000, 8}, + {0xc000, 11}, + {0xc000, 12}, + {0xc000, 14}, + {0xc000, 15}, + {0xc000, 16}, + {0xc000, 17}, + {0xc000, 18}, + }, + /* 225 */ + { + {0x8002, 254}, + {0x8009, 254}, + {0x8017, 254}, + {0xc028, 254}, + {0x8001, 2}, + {0xc016, 2}, + {0x8001, 3}, + {0xc016, 3}, + {0x8001, 4}, + {0xc016, 4}, + {0x8001, 5}, + {0xc016, 5}, + {0x8001, 6}, + {0xc016, 6}, + {0x8001, 7}, + {0xc016, 7}, + }, + /* 226 */ + { + {0x8003, 254}, + {0x8006, 254}, + {0x800a, 254}, + {0x800f, 254}, + {0x8018, 254}, + {0x801f, 254}, + {0x8029, 254}, + {0xc038, 254}, + {0x8002, 2}, + {0x8009, 2}, + {0x8017, 2}, + {0xc028, 2}, + {0x8002, 3}, + {0x8009, 3}, + {0x8017, 3}, + {0xc028, 3}, + }, + /* 227 */ + { + {0x8003, 2}, + {0x8006, 2}, + {0x800a, 2}, + {0x800f, 2}, + {0x8018, 2}, + {0x801f, 2}, + {0x8029, 2}, + {0xc038, 2}, + {0x8003, 3}, + {0x8006, 3}, + {0x800a, 3}, + {0x800f, 3}, + {0x8018, 3}, + {0x801f, 3}, + {0x8029, 3}, + {0xc038, 3}, + }, + /* 228 */ + { + {0x8002, 4}, + {0x8009, 4}, + {0x8017, 4}, + {0xc028, 4}, + {0x8002, 5}, + {0x8009, 5}, + {0x8017, 5}, + {0xc028, 5}, + {0x8002, 6}, + {0x8009, 6}, + {0x8017, 6}, + {0xc028, 6}, + {0x8002, 7}, + {0x8009, 7}, + {0x8017, 7}, + {0xc028, 7}, + }, + /* 229 */ + { + {0x8003, 4}, + {0x8006, 4}, + {0x800a, 4}, + {0x800f, 4}, + {0x8018, 4}, + {0x801f, 4}, + {0x8029, 4}, + {0xc038, 4}, + {0x8003, 5}, + {0x8006, 5}, + {0x800a, 5}, + {0x800f, 5}, + {0x8018, 5}, + {0x801f, 5}, + {0x8029, 5}, + {0xc038, 5}, + }, + /* 230 */ + { + {0x8003, 6}, + {0x8006, 6}, + {0x800a, 6}, + {0x800f, 6}, + {0x8018, 6}, + {0x801f, 6}, + {0x8029, 6}, + {0xc038, 6}, + {0x8003, 7}, + {0x8006, 7}, + {0x800a, 7}, + {0x800f, 7}, + {0x8018, 7}, + {0x801f, 7}, + {0x8029, 7}, + {0xc038, 7}, + }, + /* 231 */ + { + {0x8001, 8}, + {0xc016, 8}, + {0x8001, 11}, + {0xc016, 11}, + {0x8001, 12}, + {0xc016, 12}, + {0x8001, 14}, + {0xc016, 14}, + {0x8001, 15}, + {0xc016, 15}, + {0x8001, 16}, + {0xc016, 16}, + {0x8001, 17}, + {0xc016, 17}, + {0x8001, 18}, + {0xc016, 18}, + }, + /* 232 */ + { + {0x8002, 8}, + {0x8009, 8}, + {0x8017, 8}, + {0xc028, 8}, + {0x8002, 11}, + {0x8009, 11}, + {0x8017, 11}, + {0xc028, 11}, + {0x8002, 12}, + {0x8009, 12}, + {0x8017, 12}, + {0xc028, 12}, + {0x8002, 14}, + {0x8009, 14}, + {0x8017, 14}, + {0xc028, 14}, + }, + /* 233 */ + { + {0x8003, 8}, + {0x8006, 8}, + {0x800a, 8}, + {0x800f, 8}, + {0x8018, 8}, + {0x801f, 8}, + {0x8029, 8}, + {0xc038, 8}, + {0x8003, 11}, + {0x8006, 11}, + {0x800a, 11}, + {0x800f, 11}, + {0x8018, 11}, + {0x801f, 11}, + {0x8029, 11}, + {0xc038, 11}, + }, + /* 234 */ + { + {0x8003, 12}, + {0x8006, 12}, + {0x800a, 12}, + {0x800f, 12}, + {0x8018, 12}, + {0x801f, 12}, + {0x8029, 12}, + {0xc038, 12}, + {0x8003, 14}, + {0x8006, 14}, + {0x800a, 14}, + {0x800f, 14}, + {0x8018, 14}, + {0x801f, 14}, + {0x8029, 14}, + {0xc038, 14}, + }, + /* 235 */ + { + {0x8002, 15}, + {0x8009, 15}, + {0x8017, 15}, + {0xc028, 15}, + {0x8002, 16}, + {0x8009, 16}, + {0x8017, 16}, + {0xc028, 16}, + {0x8002, 17}, + {0x8009, 17}, + {0x8017, 17}, + {0xc028, 17}, + {0x8002, 18}, + {0x8009, 18}, + {0x8017, 18}, + {0xc028, 18}, + }, + /* 236 */ + { + {0x8003, 15}, + {0x8006, 15}, + {0x800a, 15}, + {0x800f, 15}, + {0x8018, 15}, + {0x801f, 15}, + {0x8029, 15}, + {0xc038, 15}, + {0x8003, 16}, + {0x8006, 16}, + {0x800a, 16}, + {0x800f, 16}, + {0x8018, 16}, + {0x801f, 16}, + {0x8029, 16}, + {0xc038, 16}, + }, + /* 237 */ + { + {0x8003, 17}, + {0x8006, 17}, + {0x800a, 17}, + {0x800f, 17}, + {0x8018, 17}, + {0x801f, 17}, + {0x8029, 17}, + {0xc038, 17}, + {0x8003, 18}, + {0x8006, 18}, + {0x800a, 18}, + {0x800f, 18}, + {0x8018, 18}, + {0x801f, 18}, + {0x8029, 18}, + {0xc038, 18}, + }, + /* 238 */ + { + {0xc000, 19}, + {0xc000, 20}, + {0xc000, 21}, + {0xc000, 23}, + {0xc000, 24}, + {0xc000, 25}, + {0xc000, 26}, + {0xc000, 27}, + {0xc000, 28}, + {0xc000, 29}, + {0xc000, 30}, + {0xc000, 31}, + {0xc000, 127}, + {0xc000, 220}, + {0xc000, 249}, + {0xfd, 0}, + }, + /* 239 */ + { + {0x8001, 19}, + {0xc016, 19}, + {0x8001, 20}, + {0xc016, 20}, + {0x8001, 21}, + {0xc016, 21}, + {0x8001, 23}, + {0xc016, 23}, + {0x8001, 24}, + {0xc016, 24}, + {0x8001, 25}, + {0xc016, 25}, + {0x8001, 26}, + {0xc016, 26}, + {0x8001, 27}, + {0xc016, 27}, + }, + /* 240 */ + { + {0x8002, 19}, + {0x8009, 19}, + {0x8017, 19}, + {0xc028, 19}, + {0x8002, 20}, + {0x8009, 20}, + {0x8017, 20}, + {0xc028, 20}, + {0x8002, 21}, + {0x8009, 21}, + {0x8017, 21}, + {0xc028, 21}, + {0x8002, 23}, + {0x8009, 23}, + {0x8017, 23}, + {0xc028, 23}, + }, + /* 241 */ + { + {0x8003, 19}, + {0x8006, 19}, + {0x800a, 19}, + {0x800f, 19}, + {0x8018, 19}, + {0x801f, 19}, + {0x8029, 19}, + {0xc038, 19}, + {0x8003, 20}, + {0x8006, 20}, + {0x800a, 20}, + {0x800f, 20}, + {0x8018, 20}, + {0x801f, 20}, + {0x8029, 20}, + {0xc038, 20}, + }, + /* 242 */ + { + {0x8003, 21}, + {0x8006, 21}, + {0x800a, 21}, + {0x800f, 21}, + {0x8018, 21}, + {0x801f, 21}, + {0x8029, 21}, + {0xc038, 21}, + {0x8003, 23}, + {0x8006, 23}, + {0x800a, 23}, + {0x800f, 23}, + {0x8018, 23}, + {0x801f, 23}, + {0x8029, 23}, + {0xc038, 23}, + }, + /* 243 */ + { + {0x8002, 24}, + {0x8009, 24}, + {0x8017, 24}, + {0xc028, 24}, + {0x8002, 25}, + {0x8009, 25}, + {0x8017, 25}, + {0xc028, 25}, + {0x8002, 26}, + {0x8009, 26}, + {0x8017, 26}, + {0xc028, 26}, + {0x8002, 27}, + {0x8009, 27}, + {0x8017, 27}, + {0xc028, 27}, + }, + /* 244 */ + { + {0x8003, 24}, + {0x8006, 24}, + {0x800a, 24}, + {0x800f, 24}, + {0x8018, 24}, + {0x801f, 24}, + {0x8029, 24}, + {0xc038, 24}, + {0x8003, 25}, + {0x8006, 25}, + {0x800a, 25}, + {0x800f, 25}, + {0x8018, 25}, + {0x801f, 25}, + {0x8029, 25}, + {0xc038, 25}, + }, + /* 245 */ + { + {0x8003, 26}, + {0x8006, 26}, + {0x800a, 26}, + {0x800f, 26}, + {0x8018, 26}, + {0x801f, 26}, + {0x8029, 26}, + {0xc038, 26}, + {0x8003, 27}, + {0x8006, 27}, + {0x800a, 27}, + {0x800f, 27}, + {0x8018, 27}, + {0x801f, 27}, + {0x8029, 27}, + {0xc038, 27}, + }, + /* 246 */ + { + {0x8001, 28}, + {0xc016, 28}, + {0x8001, 29}, + {0xc016, 29}, + {0x8001, 30}, + {0xc016, 30}, + {0x8001, 31}, + {0xc016, 31}, + {0x8001, 127}, + {0xc016, 127}, + {0x8001, 220}, + {0xc016, 220}, + {0x8001, 249}, + {0xc016, 249}, + {0xfe, 0}, + {0xff, 0}, + }, + /* 247 */ + { + {0x8002, 28}, + {0x8009, 28}, + {0x8017, 28}, + {0xc028, 28}, + {0x8002, 29}, + {0x8009, 29}, + {0x8017, 29}, + {0xc028, 29}, + {0x8002, 30}, + {0x8009, 30}, + {0x8017, 30}, + {0xc028, 30}, + {0x8002, 31}, + {0x8009, 31}, + {0x8017, 31}, + {0xc028, 31}, + }, + /* 248 */ + { + {0x8003, 28}, + {0x8006, 28}, + {0x800a, 28}, + {0x800f, 28}, + {0x8018, 28}, + {0x801f, 28}, + {0x8029, 28}, + {0xc038, 28}, + {0x8003, 29}, + {0x8006, 29}, + {0x800a, 29}, + {0x800f, 29}, + {0x8018, 29}, + {0x801f, 29}, + {0x8029, 29}, + {0xc038, 29}, + }, + /* 249 */ + { + {0x8003, 30}, + {0x8006, 30}, + {0x800a, 30}, + {0x800f, 30}, + {0x8018, 30}, + {0x801f, 30}, + {0x8029, 30}, + {0xc038, 30}, + {0x8003, 31}, + {0x8006, 31}, + {0x800a, 31}, + {0x800f, 31}, + {0x8018, 31}, + {0x801f, 31}, + {0x8029, 31}, + {0xc038, 31}, + }, + /* 250 */ + { + {0x8002, 127}, + {0x8009, 127}, + {0x8017, 127}, + {0xc028, 127}, + {0x8002, 220}, + {0x8009, 220}, + {0x8017, 220}, + {0xc028, 220}, + {0x8002, 249}, + {0x8009, 249}, + {0x8017, 249}, + {0xc028, 249}, + {0xc000, 10}, + {0xc000, 13}, + {0xc000, 22}, + {0x100, 0}, + }, + /* 251 */ + { + {0x8003, 127}, + {0x8006, 127}, + {0x800a, 127}, + {0x800f, 127}, + {0x8018, 127}, + {0x801f, 127}, + {0x8029, 127}, + {0xc038, 127}, + {0x8003, 220}, + {0x8006, 220}, + {0x800a, 220}, + {0x800f, 220}, + {0x8018, 220}, + {0x801f, 220}, + {0x8029, 220}, + {0xc038, 220}, + }, + /* 252 */ + { + {0x8003, 249}, + {0x8006, 249}, + {0x800a, 249}, + {0x800f, 249}, + {0x8018, 249}, + {0x801f, 249}, + {0x8029, 249}, + {0xc038, 249}, + {0x8001, 10}, + {0xc016, 10}, + {0x8001, 13}, + {0xc016, 13}, + {0x8001, 22}, + {0xc016, 22}, + {0x100, 0}, + {0x100, 0}, + }, + /* 253 */ + { + {0x8002, 10}, + {0x8009, 10}, + {0x8017, 10}, + {0xc028, 10}, + {0x8002, 13}, + {0x8009, 13}, + {0x8017, 13}, + {0xc028, 13}, + {0x8002, 22}, + {0x8009, 22}, + {0x8017, 22}, + {0xc028, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 254 */ + { + {0x8003, 10}, + {0x8006, 10}, + {0x800a, 10}, + {0x800f, 10}, + {0x8018, 10}, + {0x801f, 10}, + {0x8029, 10}, + {0xc038, 10}, + {0x8003, 13}, + {0x8006, 13}, + {0x800a, 13}, + {0x800f, 13}, + {0x8018, 13}, + {0x801f, 13}, + {0x8029, 13}, + {0xc038, 13}, + }, + /* 255 */ + { + {0x8003, 22}, + {0x8006, 22}, + {0x800a, 22}, + {0x800f, 22}, + {0x8018, 22}, + {0x801f, 22}, + {0x8029, 22}, + {0xc038, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 256 */ + { + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, }; diff --git a/thirdparty/nghttp2/nghttp2_helper.c b/thirdparty/nghttp2/nghttp2_helper.c index 9dbfa7ddfda..8ee736af877 100644 --- a/thirdparty/nghttp2/nghttp2_helper.c +++ b/thirdparty/nghttp2/nghttp2_helper.c @@ -103,6 +103,9 @@ const char *nghttp2_strerror(int error_code) { return "Internal error"; case NGHTTP2_ERR_CANCEL: return "Cancel"; + case NGHTTP2_ERR_SETTINGS_EXPECTED: + return "When a local endpoint expects to receive SETTINGS frame, it " + "receives an other type of frame"; case NGHTTP2_ERR_NOMEM: return "Out of memory"; case NGHTTP2_ERR_CALLBACK_FAILURE: @@ -112,6 +115,10 @@ const char *nghttp2_strerror(int error_code) { case NGHTTP2_ERR_FLOODED: return "Flooding was detected in this HTTP/2 session, and it must be " "closed"; + case NGHTTP2_ERR_TOO_MANY_SETTINGS: + return "SETTINGS frame contained more than the maximum allowed entries"; + case NGHTTP2_ERR_TOO_MANY_CONTINUATIONS: + return "Too many CONTINUATION frames following a HEADER frame"; default: return "Unknown error code"; } diff --git a/thirdparty/nghttp2/nghttp2_mem.c b/thirdparty/nghttp2/nghttp2_mem.c index 10c31ccf734..6a449cffd70 100644 --- a/thirdparty/nghttp2/nghttp2_mem.c +++ b/thirdparty/nghttp2/nghttp2_mem.c @@ -25,17 +25,26 @@ #include "nghttp2_mem.h" static void *default_malloc(size_t size, void *mem_user_data) { + (void)mem_user_data; + return malloc(size); } -static void default_free(void *ptr, void *mem_user_data) { free(ptr); } +static void default_free(void *ptr, void *mem_user_data) { + (void)mem_user_data; + + free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { + (void)mem_user_data; -static void *default_calloc(size_t nmemb, size_t size, - void *mem_user_data) { return calloc(nmemb, size); } static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { + (void)mem_user_data; + return realloc(ptr, size); } diff --git a/thirdparty/nghttp2/nghttp2ver.h b/thirdparty/nghttp2/nghttp2ver.h new file mode 100644 index 00000000000..827c9989684 --- /dev/null +++ b/thirdparty/nghttp2/nghttp2ver.h @@ -0,0 +1,42 @@ +/* + * nghttp2 - HTTP/2 C Library + * + * Copyright (c) 2012, 2013 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGHTTP2VER_H +#define NGHTTP2VER_H + +/** + * @macro + * Version number of the nghttp2 library release + */ +#define NGHTTP2_VERSION "1.64.0" + +/** + * @macro + * Numerical representation of the version number of the nghttp2 library + * release. This is a 24 bit number with 8 bits for major number, 8 bits + * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203. + */ +#define NGHTTP2_VERSION_NUM 0x014000 + +#endif /* NGHTTP2VER_H */ diff --git a/thirdparty/nlohmann/LICENSE.MIT b/thirdparty/nlohmann/LICENSE.MIT new file mode 100644 index 00000000000..f0622d6dc24 --- /dev/null +++ b/thirdparty/nlohmann/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013-2021 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/thirdparty/nlohmann/adl_serializer.hpp b/thirdparty/nlohmann/adl_serializer.hpp new file mode 100644 index 00000000000..eeaa1425740 --- /dev/null +++ b/thirdparty/nlohmann/adl_serializer.hpp @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include +#include + +namespace nlohmann +{ + +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static auto from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static auto to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; + +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/conversions/from_json.hpp b/thirdparty/nlohmann/detail/conversions/from_json.hpp new file mode 100644 index 00000000000..c389dca7ad3 --- /dev/null +++ b/thirdparty/nlohmann/detail/conversions/from_json.hpp @@ -0,0 +1,389 @@ +#pragma once + +#include // transform +#include // array +#include // and, not +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template::value and + not std::is_same::value, + int> = 0> +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value and + not std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + l.resize(j.size()); + std::copy(j.begin(), j.end(), std::begin(l)); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template ::value and + not is_constructible_object_type::value and + not is_constructible_string_type::value and + not is_basic_json::value, + int > = 0 > + +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + + std::string(j.type_name()))); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); + } + + ConstructibleObjectType ret; + auto inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value and + not std::is_same::value, + int> = 0> +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); + } +} + +template +void from_json(const BasicJsonType& j, std::pair& p) +{ + p = {j.at(0).template get(), j.at(1).template get()}; +} + +template +void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) +{ + t = std::make_tuple(j.at(Idx).template get::type>()...); +} + +template +void from_json(const BasicJsonType& j, std::tuple& t) +{ + from_json_tuple_impl(j, t, index_sequence_for {}); +} + +template ::value>> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template ::value>> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(not j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(not p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T& val) const + noexcept(noexcept(from_json(j, val))) + -> decltype(from_json(j, val), void()) + { + return from_json(j, val); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace +{ +constexpr const auto& from_json = detail::static_const::value; +} // namespace +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/conversions/to_chars.hpp b/thirdparty/nlohmann/detail/conversions/to_chars.hpp new file mode 100644 index 00000000000..d99703a54c1 --- /dev/null +++ b/thirdparty/nlohmann/detail/conversions/to_chars.hpp @@ -0,0 +1,1106 @@ +#pragma once + +#include // array +#include // assert +#include // or, and, not +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional +#include + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + assert(x.e == y.e); + assert(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + assert(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + assert(delta >= 0); + assert(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + assert(std::isfinite(value)); + assert(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const std::uint64_t bits = reinterpret_bits(value); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 and E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + assert(e >= -1500); + assert(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + assert(index >= 0); + assert(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + assert(kAlpha <= cached.e + e + 64); + assert(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + else if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + else if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + else if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + else if (n >= 100000) + { + pow10 = 100000; + return 6; + } + else if (n >= 10000) + { + pow10 = 10000; + return 5; + } + else if (n >= 1000) + { + pow10 = 1000; + return 4; + } + else if (n >= 100) + { + pow10 = 100; + return 3; + } + else if (n >= 10) + { + pow10 = 10; + return 2; + } + else + { + pow10 = 1; + return 1; + } +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + assert(len >= 1); + assert(dist <= delta); + assert(rest <= delta); + assert(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + and delta - rest >= ten_k + and (rest + ten_k < dist or dist - rest > rest + ten_k - dist)) + { + assert(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + assert(M_plus.e >= kAlpha); + assert(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + assert(p1 > 0); + + std::uint32_t pow10; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + assert(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + assert(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + assert(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + assert(m_plus.e == m_minus.e); + assert(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + assert(std::isfinite(value)); + assert(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + assert(e > -1000); + assert(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + assert(min_exp < 0); + assert(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n and n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n - k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (n + 2); + } + + if (0 < n and n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + assert(k > n); + + std::memmove(buf + (n + 1), buf + n, static_cast(k - n)); + buf[n] = '.'; + return buf + (k + 1); + } + + if (min_exp < n and n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + -n), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2 + (-n) + k); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k - 1)); + buf[1] = '.'; + buf += 1 + k; + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + assert(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } + + assert(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + assert(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + assert(last - first >= kMaxExp + 2); + assert(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + assert(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/conversions/to_json.hpp b/thirdparty/nlohmann/detail/conversions/to_json.hpp new file mode 100644 index 00000000000..a1def699fd7 --- /dev/null +++ b/thirdparty/nlohmann/detail/conversions/to_json.hpp @@ -0,0 +1,347 @@ +#pragma once + +#include // copy +#include // or, and, not +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_type = value_t::array; + j.m_value = arr; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.assert_invariant(); + } + + template::value, + int> = 0> + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_type = value_t::object; + j.m_value = obj; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template ::value and + not is_compatible_object_type< + BasicJsonType, CompatibleArrayType>::value and + not is_compatible_string_type::value and + not is_basic_json::value, + int> = 0> +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template::value and not is_basic_json::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t::value, + int> = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) +{ + external_constructor::construct(j, arr); +} + +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template < typename BasicJsonType, typename T, + enable_if_t>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +} // namespace +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/exceptions.hpp b/thirdparty/nlohmann/detail/exceptions.hpp new file mode 100644 index 00000000000..ed836188ccc --- /dev/null +++ b/thirdparty/nlohmann/detail/exceptions.hpp @@ -0,0 +1,356 @@ +#pragma once + +#include // exception +#include // runtime_error +#include // to_string + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors + +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal + +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + JSON_HEDLEY_RETURNS_NON_NULL + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + static parse_error create(int id_, const position_t& pos, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); + } + + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + static invalid_iterator create(int id_, const std::string& what_arg) + { + std::string w = exception::name("invalid_iterator", id_) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | +json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + static type_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("type_error", id_) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + static out_of_range create(int id_, const std::string& what_arg) + { + std::string w = exception::name("out_of_range", id_) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/https/github.com/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + static other_error create(int id_, const std::string& what_arg) + { + std::string w = exception::name("other_error", id_) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/binary_reader.hpp b/thirdparty/nlohmann/detail/input/binary_reader.hpp new file mode 100644 index 00000000000..1b6e0f9b7b3 --- /dev/null +++ b/thirdparty/nlohmann/detail/input/binary_reader.hpp @@ -0,0 +1,1983 @@ +#pragma once + +#include // generate_n +#include // array +#include // assert +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move + +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using json_sax_t = SAX; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(input_adapter_t adapter) : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + assert(ia); + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + + @return + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result and strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"))); + } + } + + return result; + } + + /*! + @brief determine system byte order + + @return true if and only if system's byte order is little endian + + @note from http://stackoverflow.com/a/1001328/266378 + */ + static constexpr bool little_endianess(int num = 1) noexcept + { + return *reinterpret_cast(&num) == 1; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + + return true; + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in, out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"))); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) and get() != std::char_traits::eof(); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const int element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number; + return get_number(input_format_t::bson, number) and sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len; + string_t value; + return get_number(input_format_t::bson, len) and get_bson_string(len, value) and sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value; + return get_number(input_format_t::bson, value) and sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value; + return get_number(input_format_t::bson, value) and sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()))); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + while (int element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(not get_bson_cstr(key))) + { + return false; + } + + if (not is_array and not sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char = true) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number; + return get_number(input_format_t::cbor, number) and sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number; + return get_number(input_format_t::cbor, number) and sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) and sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu)); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_cbor_array(static_cast(len)); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(std::size_t(-1)); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu)); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_cbor_object(static_cast(len)); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(std::size_t(-1)); + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const int byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const int byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + assert(0 <= exp and exp <= 32); + assert(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number; + return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number; + return get_number(input_format_t::cbor, number) and sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len; + return get_number(input_format_t::cbor, len) and get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (not get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"))); + } + } + } + + /*! + @param[in] len the length of the array or std::size_t(-1) for an + array of indefinite size + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) + { + return false; + } + + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal(false))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or std::size_t(-1) for an + object of indefinite size + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) + { + return false; + } + + string_t key; + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(not get_cbor_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_cbor_internal())) + { + return false; + } + key.clear(); + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) and sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xCA: // float 32 + { + float number; + return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number; + return get_number(input_format_t::msgpack, number) and sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number; + return get_number(input_format_t::msgpack, number) and sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number; + return get_number(input_format_t::msgpack, number) and sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len; + return get_number(input_format_t::msgpack, len) and get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"))); + } + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not get_msgpack_string(key) or not sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(not parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len; + return get_number(input_format_t::ubjson, len) and get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"))); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'I': + { + std::int16_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number; + if (JSON_HEDLEY_UNLIKELY(not get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"))); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"))); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const int prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number; + return get_number(input_format_t::ubjson, number) and sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'I': + { + std::int16_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'l': + { + std::int32_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'L': + { + std::int64_t number; + return get_number(input_format_t::ubjson, number) and sax->number_integer(number); + } + + case 'd': + { + float number; + return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number; + return get_number(input_format_t::ubjson, number) and sax->number_float(static_cast(number), ""); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"))); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) and sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"))); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(not get_ubjson_string(key, false) or not sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(not parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + int get() + { + ++chars_read; + return current = ia->get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + int get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianess, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + std::generate_n(std::back_inserter(result), len, [this, &success, &format]() + { + get(); + if (JSON_HEDLEY_UNLIKELY(not unexpect_eof(format, "string"))) + { + success = false; + } + return static_cast(current); + }); + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context))); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + input_adapter_t ia = nullptr; + + /// the current character + int current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/input_adapters.hpp b/thirdparty/nlohmann/detail/input/input_adapters.hpp new file mode 100644 index 00000000000..9512a771eef --- /dev/null +++ b/thirdparty/nlohmann/detail/input/input_adapters.hpp @@ -0,0 +1,442 @@ +#pragma once + +#include // array +#include // assert +#include // size_t +#include //FILE * +#include // strlen +#include // istream +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +/*! +@brief abstract input adapter interface + +Produces a stream of std::char_traits::int_type characters from a +std::istream, a buffer, or some other input type. Accepts the return of +exactly one non-EOF character for future input. The int_type characters +returned consist of all valid char values as positive values (typically +unsigned char), plus an EOF value outside that range, specified by the value +of the function std::char_traits::eof(). This value is typically -1, but +could be any arbitrary value which is not a valid char value. +*/ +struct input_adapter_protocol +{ + /// get a character [0,255] or std::char_traits::eof(). + virtual std::char_traits::int_type get_character() = 0; + virtual ~input_adapter_protocol() = default; +}; + +/// a type to simplify interfaces +using input_adapter_t = std::shared_ptr; + +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter : public input_adapter_protocol +{ + public: + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = default; + ~file_input_adapter() override = default; + + std::char_traits::int_type get_character() noexcept override + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter : public input_adapter_protocol +{ + public: + ~input_stream_adapter() override + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + is.clear(is.rdstate() & std::ios::eofbit); + } + + explicit input_stream_adapter(std::istream& i) + : is(i), sb(*i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter(input_stream_adapter&&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, eg. 0xFFFFFFFF. + std::char_traits::int_type get_character() override + { + auto res = sb.sbumpc(); + // set eof manually, as we don't use the istream interface. + if (res == EOF) + { + is.clear(is.rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream& is; + std::streambuf& sb; +}; + +/// input adapter for buffer input +class input_buffer_adapter : public input_adapter_protocol +{ + public: + input_buffer_adapter(const char* b, const std::size_t l) noexcept + : cursor(b), limit(b == nullptr ? nullptr : (b + l)) + {} + + // delete because of pointer members + input_buffer_adapter(const input_buffer_adapter&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&) = delete; + input_buffer_adapter(input_buffer_adapter&&) = delete; + input_buffer_adapter& operator=(input_buffer_adapter&&) = delete; + ~input_buffer_adapter() override = default; + + std::char_traits::int_type get_character() noexcept override + { + if (JSON_HEDLEY_LIKELY(cursor < limit)) + { + assert(cursor != nullptr and limit != nullptr); + return std::char_traits::to_int_type(*(cursor++)); + } + + return std::char_traits::eof(); + } + + private: + /// pointer to the current character + const char* cursor; + /// pointer past the last character + const char* const limit; +}; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(const WideStringType& str, + size_t& current_wchar, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (current_wchar == str.size()) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = static_cast(str[current_wchar++]); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((wc >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(const WideStringType& str, + size_t& current_wchar, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (current_wchar == str.size()) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = static_cast(str[current_wchar++]); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((wc >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc or wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((wc >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((wc >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (wc & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (current_wchar < str.size()) + { + const auto wc2 = static_cast(str[current_wchar++]); + const auto charcode = 0x10000u + (((wc & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + ++current_wchar; + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +template +class wide_string_input_adapter : public input_adapter_protocol +{ + public: + explicit wide_string_input_adapter(const WideStringType& w) noexcept + : str(w) + {} + + std::char_traits::int_type get_character() noexcept override + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index == 0); + } + + // use buffer + assert(utf8_bytes_filled > 0); + assert(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(str, current_wchar, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// the wstring to process + const WideStringType& str; + + /// index of the current wchar in str + std::size_t current_wchar = 0; + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + +class input_adapter +{ + public: + // native support + JSON_HEDLEY_NON_NULL(2) + input_adapter(std::FILE* file) + : ia(std::make_shared(file)) {} + /// input adapter for input stream + input_adapter(std::istream& i) + : ia(std::make_shared(i)) {} + + /// input adapter for input stream + input_adapter(std::istream&& i) + : ia(std::make_shared(i)) {} + + input_adapter(const std::wstring& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u16string& ws) + : ia(std::make_shared>(ws)) {} + + input_adapter(const std::u32string& ws) + : ia(std::make_shared>(ws)) {} + + /// input adapter for buffer + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b, std::size_t l) + : ia(std::make_shared(reinterpret_cast(b), l)) {} + + // derived support + + /// input adapter for string literal + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, + int>::type = 0> + input_adapter(CharT b) + : input_adapter(reinterpret_cast(b), + std::strlen(reinterpret_cast(b))) {} + + /// input adapter for iterator range with contiguous storage + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + input_adapter(IteratorType first, IteratorType last) + { +#ifndef NDEBUG + // assertion to check that the iterator range is indeed contiguous, + // see http://stackoverflow.com/a/35008842/266378 for more discussion + const auto is_contiguous = std::accumulate( + first, last, std::pair(true, 0), + [&first](std::pair res, decltype(*first) val) + { + res.first &= (val == *(std::next(std::addressof(*first), res.second++))); + return res; + }).first; + assert(is_contiguous); +#endif + + // assertion to check that each element is 1 byte long + static_assert( + sizeof(typename iterator_traits::value_type) == 1, + "each element in the iterator range must have the size of 1 byte"); + + const auto len = static_cast(std::distance(first, last)); + if (JSON_HEDLEY_LIKELY(len > 0)) + { + // there is at least one element: use the address of first + ia = std::make_shared(reinterpret_cast(&(*first)), len); + } + else + { + // the address of first cannot be used: use nullptr + ia = std::make_shared(nullptr, len); + } + } + + /// input adapter for array + template + input_adapter(T (&array)[N]) + : input_adapter(std::begin(array), std::end(array)) {} + + /// input adapter for contiguous container + template::value and + std::is_base_of()))>::iterator_category>::value, + int>::type = 0> + input_adapter(const ContiguousContainer& c) + : input_adapter(std::begin(c), std::end(c)) {} + + operator input_adapter_t() + { + return ia; + } + + private: + /// the actual adapter + input_adapter_t ia = nullptr; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/json_sax.hpp b/thirdparty/nlohmann/detail/input/json_sax.hpp new file mode 100644 index 00000000000..606b7862ebd --- /dev/null +++ b/thirdparty/nlohmann/detail/input/json_sax.hpp @@ -0,0 +1,701 @@ +#pragma once + +#include // assert +#include +#include // string +#include // move +#include // vector + +#include +#include + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + /// type for (signed) integers + using number_integer_t = typename BasicJsonType::number_integer_t; + /// type for unsigned integers + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + /// type for floating-point numbers + using number_float_t = typename BasicJsonType::number_float_t; + /// type for strings + using string_t = typename BasicJsonType::string_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief an floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + /*! + @param[in, out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, + "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + ref_stack.pop_back(); + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const detail::exception& ex) + { + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) + { + case 1: + JSON_THROW(*static_cast(&ex)); + case 4: + JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + JSON_THROW(*static_cast(&ex)); + case 3: + JSON_THROW(*static_cast(&ex)); + case 5: + JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP + } + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + assert(ref_stack.back()->is_object()); + assert(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep and ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back() and not callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (not ref_stack.empty() and ref_stack.back() and ref_stack.back()->is_object()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() and JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) and len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (not keep) + { + // discard array + *ref_stack.back() = discarded; + } + } + + assert(not ref_stack.empty()); + assert(not keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (not keep and not ref_stack.empty() and ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const detail::exception& ex) + { + errored = true; + if (allow_exceptions) + { + // determine the proper exception type from the id + switch ((ex.id / 100) % 100) + { + case 1: + JSON_THROW(*static_cast(&ex)); + case 4: + JSON_THROW(*static_cast(&ex)); + // LCOV_EXCL_START + case 2: + JSON_THROW(*static_cast(&ex)); + case 3: + JSON_THROW(*static_cast(&ex)); + case 5: + JSON_THROW(*static_cast(&ex)); + default: + assert(false); + // LCOV_EXCL_STOP + } + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + assert(not keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (not keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback or callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (not keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (not ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + assert(ref_stack.back()->is_array() or ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->push_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + assert(ref_stack.back()->is_object()); + // check if we should store an element for the current key + assert(not key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (not store_element) + { + return {false, nullptr}; + } + + assert(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/lexer.hpp b/thirdparty/nlohmann/detail/input/lexer.hpp new file mode 100644 index 00000000000..0843d749d6a --- /dev/null +++ b/thirdparty/nlohmann/detail/input/lexer.hpp @@ -0,0 +1,1512 @@ +#pragma once + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case lexer::token_type::value_unsigned: + case lexer::token_type::value_integer: + case lexer::token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } + + explicit lexer(detail::input_adapter_t&& adapter) + : ia(std::move(adapter)), decimal_point_char(get_decimal_point()) {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = delete; + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = delete; + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto loc = localeconv(); + assert(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + assert(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' and current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' and current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' and current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + assert(0x0000 <= codepoint and codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + assert(ranges.size() == 2 or ranges.size() == 4 or ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current and current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 7159. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + assert(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 and codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' and get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 and codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 and codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + assert(0x00 <= codepoint and codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(codepoint); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(not next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(not (next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 7159. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 7159. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | [error] | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + assert(false); // LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + assert(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char* literal_text, const std::size_t length, + token_type return_type) + { + assert(current == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(get() != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + std::char_traits::int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia->get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + assert(not token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(int c) + { + token_buffer.push_back(std::char_traits::to_char_type(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if ('\x00' <= c and c <= '\x1F') + { + // escape control characters + std::array cs{{}}; + (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); + result += cs.data(); + } + else + { + // add character as is + result.push_back(c); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB and get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 and not skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + do + { + get(); + } + while (current == ' ' or current == '\t' or current == '\n' or current == '\r'); + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + return scan_literal("true", 4, token_type::literal_true); + case 'f': + return scan_literal("false", 5, token_type::literal_false); + case 'n': + return scan_literal("null", 4, token_type::literal_null); + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + detail::input_adapter_t ia = nullptr; + + /// the current character + std::char_traits::int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/parser.hpp b/thirdparty/nlohmann/detail/input/parser.hpp new file mode 100644 index 00000000000..8d4febcbfab --- /dev/null +++ b/thirdparty/nlohmann/detail/input/parser.hpp @@ -0,0 +1,498 @@ +#pragma once + +#include // assert +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +#include +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +/*! +@brief syntax analysis + +This class implements a recursive decent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + enum class parse_event_t : uint8_t + { + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value + }; + + using parser_callback_t = + std::function; + + /// a parser reading from an input adapter + explicit parser(detail::input_adapter_t&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true) + : callback(cb), m_lexer(std::move(adapter)), allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict and (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + result.assert_invariant(); + + // in strict mode, input must be completely read + if (strict and (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result and strict and (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"))); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (not skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_object(std::size_t(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(not sax->start_array(std::size_t(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(not std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'")); + } + + if (JSON_HEDLEY_UNLIKELY(not sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(not sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(not sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(not sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(not sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(not sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(not sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::uninitialized, "value"))); + } + + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::literal_or_value, "value"))); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + assert(not states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_array, "array"))); + } + else // object + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::value_string, "object key"))); + } + + if (JSON_HEDLEY_UNLIKELY(not sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::name_separator, "object separator"))); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(not sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + assert(not states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_object, "object"))); + } + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (not context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/input/position_t.hpp b/thirdparty/nlohmann/detail/input/position_t.hpp new file mode 100644 index 00000000000..14e9649fb29 --- /dev/null +++ b/thirdparty/nlohmann/detail/input/position_t.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp b/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp new file mode 100644 index 00000000000..2c81f723fd2 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/internal_iterator.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/iter_impl.hpp b/thirdparty/nlohmann/detail/iterators/iter_impl.hpp new file mode 100644 index 00000000000..3a362971909 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/iter_impl.hpp @@ -0,0 +1,638 @@ +#pragma once + +#include // not +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +#include +#include +#include +#include +#include +#include +#include + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl +{ + /// allow basic_json to access private members + friend iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + /// default constructor + iter_impl() = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + private: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + assert(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + assert(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator==(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator!=(const iter_impl& other) const + { + return not operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); + } + + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return not other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return not operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return not operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + assert(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value")); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + assert(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + private: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp b/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp new file mode 100644 index 00000000000..c61d96296ce --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/iteration_proxy.hpp @@ -0,0 +1,176 @@ +#pragma once + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + target = std::to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str = ""; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept : anchor(it) {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + assert(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std diff --git a/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp b/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp new file mode 100644 index 00000000000..4cced80caf4 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/iterator_traits.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include // random_access_iterator_tag + +#include +#include + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp b/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp new file mode 100644 index 00000000000..f3b5b5db6b8 --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/json_reverse_iterator.hpp @@ -0,0 +1,119 @@ +#pragma once + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp b/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp new file mode 100644 index 00000000000..28d6f1a65df --- /dev/null +++ b/thirdparty/nlohmann/detail/iterators/primitive_iterator.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include // ptrdiff_t +#include // numeric_limits + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/json_pointer.hpp b/thirdparty/nlohmann/detail/json_pointer.hpp new file mode 100644 index 00000000000..87af3423315 --- /dev/null +++ b/thirdparty/nlohmann/detail/json_pointer.hpp @@ -0,0 +1,1011 @@ +#pragma once + +#include // all_of +#include // assert +#include // isdigit +#include // accumulate +#include // string +#include // move +#include // vector + +#include +#include +#include + +namespace nlohmann +{ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value + + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below + + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + /*! + @brief append another JSON pointer at the end of this JSON pointer + + @param[in] ptr JSON pointer to append + @return JSON pointer with @a ptr appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, const json_pointer&) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /*! + @brief append an unescaped reference token at the end of this JSON pointer + + @param[in] token reference token to append + @return JSON pointer with @a token appended without escaping @a token + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::size_t) to append an array index + @sa @ref operator/(const json_pointer&, std::size_t) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /*! + @brief append an array index at the end of this JSON pointer + + @param[in] array_index array index to append + @return JSON pointer with @a array_index appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + @sa @ref operator/=(std::string) to append a reference token + @sa @ref operator/(const json_pointer&, std::string) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::size_t array_index) + { + return *this /= std::to_string(array_index); + } + + /*! + @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + + @param[in] lhs JSON pointer + @param[in] rhs JSON pointer + @return a new JSON pointer with @a rhs appended to @a lhs + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a lhs and @a rhs. + + @sa @ref operator/=(const json_pointer&) to append a JSON pointer + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /*! + @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] token reference token + @return a new JSON pointer with unescaped @a token appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::string) to append a reference token + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::string token) + { + return json_pointer(ptr) /= std::move(token); + } + + /*! + @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] array_index array index + @return a new JSON pointer with @a array_index appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa @ref operator/=(std::size_t) to append an array index + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_index) + { + return json_pointer(ptr) /= array_index; + } + + /*! + @brief returns the parent of this JSON pointer + + @return parent of this JSON pointer; in case this JSON pointer is the root, + the root itself is returned + + @complexity Linear in the length of the JSON pointer. + + @liveexample{The example shows the result of `parent_pointer` for different + JSON Pointers.,json_pointer__parent_pointer} + + @since version 3.6.0 + */ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /*! + @brief remove last reference token + + @pre not `empty()` + + @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + reference_tokens.pop_back(); + } + + /*! + @brief return last reference token + + @pre not `empty()` + @return last reference token + + @liveexample{The example shows the usage of `back`.,json_pointer__back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + return reference_tokens.back(); + } + + /*! + @brief append an unescaped token at the end of the reference pointer + + @param[in] token token to add + + @complexity Amortized constant. + + @liveexample{The example shows the result of `push_back` for different + JSON Pointers.,json_pointer__push_back} + + @since version 3.6.0 + */ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @copydoc push_back(const std::string&) + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /*! + @brief return whether pointer points to the root document + + @return true iff the JSON pointer points to the root document + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example shows the result of `empty` for different JSON + Pointers.,json_pointer__empty} + + @since version 3.6.0 + */ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw out_of_range.404 if string @a s could not be converted to an integer + */ + static int array_index(const std::string& s) + { + std::size_t processed_chars = 0; + const int res = std::stoi(s, &processed_chars); + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'")); + } + + return res; + } + + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + using size_type = typename BasicJsonType::size_type; + auto result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + JSON_TRY + { + result = &result->operator[](static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten")); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums or reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // use unchecked array access + JSON_TRY + { + ptr = &ptr->operator[]( + static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range")); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + // note: at performs range check + JSON_TRY + { + ptr = &ptr->at(static_cast(array_index(reference_token))); + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'")); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + using size_type = typename BasicJsonType::size_type; + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (not ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1 and reference_token[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, + "array index '" + reference_token + + "' must not begin with '0'")); + } + + JSON_TRY + { + const auto idx = static_cast(array_index(reference_token)); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + JSON_CATCH(std::invalid_argument&) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + reference_token + "' is not a number")); + } + break; + } + + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'")); + } + } + + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + /*! + @brief replace all occurrences of a substring by another string + + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f + + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** + + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, + const std::string& t) + { + assert(not f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} + } + + /// escape "~" to "~0" and "/" to "~1" + static std::string escape(std::string s) + { + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; + } + + /// unescape "~1" to tilde and "~0" to slash (order is important!) + static void unescape(std::string& s) + { + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); + } + + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + escape(element.first), element.second, result); + } + } + break; + } + + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(not value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened")); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(not element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive")); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return not (lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/json_ref.hpp b/thirdparty/nlohmann/detail/json_ref.hpp new file mode 100644 index 00000000000..c8dec7330f2 --- /dev/null +++ b/thirdparty/nlohmann/detail/json_ref.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include +#include + +#include + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)), value_ref(&owned_value), is_rvalue(true) + {} + + json_ref(const value_type& value) + : value_ref(const_cast(&value)), is_rvalue(false) + {} + + json_ref(std::initializer_list init) + : owned_value(init), value_ref(&owned_value), is_rvalue(true) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...), value_ref(&owned_value), + is_rvalue(true) {} + + // class should be movable only + json_ref(json_ref&&) = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (is_rvalue) + { + return std::move(*value_ref); + } + return *value_ref; + } + + value_type const& operator*() const + { + return *static_cast(value_ref); + } + + value_type const* operator->() const + { + return static_cast(value_ref); + } + + private: + mutable value_type owned_value = nullptr; + value_type* value_ref = nullptr; + const bool is_rvalue; +}; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/macro_scope.hpp b/thirdparty/nlohmann/detail/macro_scope.hpp new file mode 100644 index 00000000000..27dddc6be76 --- /dev/null +++ b/thirdparty/nlohmann/detail/macro_scope.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include // pair +#include + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 +#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 +#endif + +// disable float-equal warnings on GCC/clang +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdocumentation" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json diff --git a/thirdparty/nlohmann/detail/macro_unscope.hpp b/thirdparty/nlohmann/detail/macro_unscope.hpp new file mode 100644 index 00000000000..80b293e7d59 --- /dev/null +++ b/thirdparty/nlohmann/detail/macro_unscope.hpp @@ -0,0 +1,21 @@ +#pragma once + +// restore GCC/clang diagnostic settings +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #pragma GCC diagnostic pop +#endif +#if defined(__clang__) + #pragma GCC diagnostic pop +#endif + +// clean up +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL + +#include diff --git a/thirdparty/nlohmann/detail/meta/cpp_future.hpp b/thirdparty/nlohmann/detail/meta/cpp_future.hpp new file mode 100644 index 00000000000..948cd4fb0cb --- /dev/null +++ b/thirdparty/nlohmann/detail/meta/cpp_future.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include // not +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type + +namespace nlohmann +{ +namespace detail +{ +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +template +using uncvref_t = typename std::remove_cv::type>::type; + +// implementation of C++14 index_sequence and affiliates +// source: https://stackoverflow.com/a/32223343 +template +struct index_sequence +{ + using type = index_sequence; + using value_type = std::size_t; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +template +struct merge_and_renumber; + +template +struct merge_and_renumber, index_sequence> + : index_sequence < I1..., (sizeof...(I1) + I2)... > {}; + +template +struct make_index_sequence + : merge_and_renumber < typename make_index_sequence < N / 2 >::type, + typename make_index_sequence < N - N / 2 >::type > {}; + +template<> struct make_index_sequence<0> : index_sequence<> {}; +template<> struct make_index_sequence<1> : index_sequence<0> {}; + +template +using index_sequence_for = make_index_sequence; + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; +} // namespace detail +} // namespace nlohmann diff --git a/thirdparty/nlohmann/detail/meta/detected.hpp b/thirdparty/nlohmann/detail/meta/detected.hpp new file mode 100644 index 00000000000..5b52460acf6 --- /dev/null +++ b/thirdparty/nlohmann/detail/meta/detected.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +// http://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template